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

vue3+vite+ts安装wangeditor富文本编辑器

需求:

实现粘贴,上传图片时本地渲染但并不实现上传功能,工具栏移除不需要的工具

安装方法看官网

安装 | wangEditor

封装子组件

wangEditor.vue

<template>
  <div>
    <div style="border: 1px solid #ccc; margin-top: 10px">
      <Toolbar
        :editor="editorRef"
        :defaultConfig="toolbarConfig"
        :mode="mode"
        style="border-bottom: 1px solid #ccc"
      />
      <Editor
        :defaultConfig="editorConfig"
        :mode="mode"
        v-model="valueHtml"
        :style="{ height: editorHeight, overflowY: 'hidden' }"
        @onCreated="handleCreated"
        @onChange="handleChange"
        @onDestroyed="handleDestroyed"
        @onFocus="handleFocus"
        @onBlur="handleBlur"
        @customPaste="customPaste"
      />
    </div>
  </div>
</template>

ts文件

export interface ViewDataType {
  visible: boolean
  item: {
    id: string
    topDate: string
    titleTime: string
    author: string
    address: string
    content1: string
    content3: string
    content5: string
    content6: string
    content7: string
    imgUrl: string
    [key: string]: string // 允许动态属性
  }
}
// 列表编辑弹框中的类型定义
export interface EditDataType {
  visible: boolean;
  datas: {
    id: string
    topDate: string
    titleTime: string
    author: string
    address: string
    imgUrl: string
    [key: string]: string // 允许动态属性
  }
  RuleForm :{
    theme: string
    beginTime: string
    content: string
  }
}
  // 上传功能
  export  type InsertFnType = (
    url: string,
    alt: string,
    href: string,
    style: string
  ) => void

js

<script lang="ts" setup>
  import '@wangeditor/editor/dist/css/style.css'
  import {
    onBeforeUnmount,
    ref,
    shallowRef,
    onMounted,
    PropType,
    nextTick,
  } from 'vue'
  import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
  import { DomEditor } from '@wangeditor/editor'
  // 接收父组件传递的参数
  const props = defineProps({
    initialValue: {
      type: String,
      default: '',
    },
    editorHeight: {
      type: String,
      default: '200px',
    },
    toolbarConfig: {
      type: Object as PropType<Record<string, any>>,
      default: () => ({}),
    },
    editorConfig: {
      type: Object as PropType<Record<string, any>>,
      default: () => ({ placeholder: '请输入内容...' }),
    },
    mode: {
      type: String,
      default: 'default',
    },
  })

  // 编辑器实例,必须用 shallowRef,重要!
  const editorRef = shallowRef<typeof Editor | null>(null)
  // 内容 HTML
  const valueHtml = ref<string>(props.initialValue)
  const emit = defineEmits([
    'handleChange',
    'handleFocus',
    'handleDestroyed',
    'handleBlur',
    'customPaste',
  ])
  // 模拟 ajax 异步获取内容
  onMounted(() => {
    // setTimeout(() => {
    //   valueHtml.value = '<p>模拟 Ajax 异步设置内容</p>'
    // }, 1500)
  })
  // 编辑器回调函数
  // const handleCreated = (editor: typeof Editor) => {
  const handleCreated = (editor) => {
    nextTick(() => {
      editorRef.value = editor // 记录 editor 实例,重要!
      const toolbar = DomEditor.getToolbar(editor)
      const curToolbarConfig = toolbar?.getConfig()
      console.log(' 当前菜单排序和分组', toolbar) // 当前菜单排序和分组
      console.log('curToolbarConfig', curToolbarConfig?.toolbarKeys) //查看工具栏内容
    })
  }

  // 触发事件实现双向绑定
  const handleChange = (editor: typeof Editor) => {
   emit('handleChange', editor.getHtml())
  }
  // 卸载事件
  const handleDestroyed = (editor: typeof Editor) => {
    emit('handleDestroyed', editor)
  }
  // 失去焦点事件
  const handleFocus = (editor: typeof Editor) => {
    emit('handleFocus', editor)
  }
  const handleBlur = (editor: typeof Editor) => {
    emit('handleBlur', editor)
  }
  // 粘贴事件
  const customPaste = (
    editor: typeof Editor,
    event: ClipboardEvent,
    callback: (value: boolean) => void
  ) => {
    emit('customPaste', event, editor, callback)
  }

  // 组件销毁时,也及时销毁编辑器,重要!
  onBeforeUnmount(() => {
    const editor = editorRef.value
    if (editor == null) return
    editor.destroy()
  })
  // 提交事件给父组件
  // const emit = defineEmits(['editor'])
  // emit('editor', editorRef.value)
  // 导出
  defineExpose({
    editorRef,
  })
</script>

父组件调用并使用

 <wangEditor
                      :initialValue="ruleForm.content"
                      :editorHeight="'200px'"
                      :toolbarConfig="toolbarConfig"
                      :editorConfig="editorConfig"
                      :mode="'default'"
                      ref="wangEditorRef"
                      @customPaste="customPaste"
                        @handleChange="handleChange"
                    />
<script lang="ts" setup>
  import { onMounted, reactive, ref, unref } from 'vue'
  import type { FormInstance, FormRules, TabsPaneContext } from 'element-plus'
  import { EditDataType, InsertFnType } from '@/types/messages/securityInfoList'
  import wangEditor from './wangEditor.vue'
  import { IToolbarConfig } from '@wangeditor/editor'

  const headerTitle = ref('编辑安全资讯')
  const props = withDefaults(defineProps<EditDataType>(), {
    visible: false,
  })
  const emit = defineEmits(['closeApply'])
  //  来获取表单实例,从而调用表单的验证方法
  const ruleFormRef = ref<FormInstance>()
  // 编辑表单数据
  const ruleForm = reactive<EditDataType['RuleForm']>({
    content: '',
  })
  // 表单数据检验
  const rules = reactive<FormRules<EditDataType['RuleForm']>>({
   
    content: [
      { required: true, message: '安全资讯内容不能为空!', trigger: 'blur' },
    ],
  })

  // 加载完成
  onMounted(() => {
    console.log('props', props.datas)
    Object.keys(ruleForm).forEach((key) => {
      ruleForm[key] = props.datas[key]
    })
  })
  const closeApply = () => {
    // 清空表单
    ruleFormRef.value?.resetFields()
    emit('closeApply')
  }
  // 定义防抖函数
  const debounce = (func: Function, delay: number) => {
    let timer: NodeJS.Timeout
    return (...args: any[]) => {
      clearTimeout(timer)
      timer = setTimeout(() => {
        func(...args)
      }, delay)
    }
  }
 // 子组件触发事件
  const handleChange = (value) => {
    console.log('子组件触发事件', value)
    ruleForm.content = value
  }
  // 定义保存按钮点击事件处理函数
  const handlerSave = () => {
    // 在这里执行保存操作的代码
  
    submitForm(ruleFormRef.value)
  } // 设置防抖时间间隔为1秒
  // 保存表单检验
  const submitForm = async (formEl: FormInstance | undefined) => {
    if (!formEl) return
    await formEl.validate((valid, fields) => {
      if (valid) {
        console.log('保存提交表单!')
        // 清空表单
        ruleFormRef.value?.resetFields()
      } else {
        console.log('缺少必填内容!', fields)
      }
    })
  }
  // 定义编辑器工具栏
  const toolbarConfig: Partial<IToolbarConfig> = {
    // 配置工具栏上移除网络上传图片,视频,表格,代码块,行内代码
    excludeKeys: [
      'insertImage',
      'group-video',
      'insertTable',
      'codeBlock',
      'code',
    ],
  }

  // 配置编辑器
  const editorConfig = ref<Record<string, any>>({
    placeholder: '请输入安全资讯内容==--父组件',
    MENU_CONF: {
      uploadImage: {
        // 选择文件时的类型限制,默认为['image/jpeg', 'image/png', 'image/gif']
        allowedFileTypes: ['image/jpeg', 'image/png', 'image/gif'],
        async customUpload(file: File, insertFn: any) {
          const reader = new FileReader()
          reader.onload = async () => {
            if (reader.result) {
              // reader.result 包含了 Base64 编码的文件数据
              const base64Data = reader.result as string
              const imgUrl = base64Data //base64传为img标签元素才能设置宽高
              const imgHtml = `<img src="${imgUrl}" alt="image" style="width:200px;height:200px;"/>`
              let editorRefs = await wangEditorRef?.value.editorRef
              const editor = unref(editorRefs)
              if (editor) {
                // 通过 dangerouslyInsertHtml 插入图片的 HTML,控制宽高
                editor.dangerouslyInsertHtml(imgHtml)
              }
              // 插入图片到编辑器
              // insertFn(imgHtml, file.name, '', '')
            }
          }
          // 读取文件并将其转换为 Base64 编码的字符串
          reader.readAsDataURL(file)
        },
      },
    },
  })
  // 粘贴事件
  const customPaste = (event, editor, callback) => {
    const items = event.clipboardData?.items
    if (!items) return
    for (let i = 0; i < items.length; i++) {
      const item = items[i]
      if (item.type.indexOf('image') !== -1) {
        const file = item.getAsFile()
        if (file) {
          // 阻止默认的上传行为
          event.preventDefault()
          // 创建一个 FileReader 来读取图片数据
          const reader = new FileReader()
          reader.onload = (e: ProgressEvent<FileReader>) => {
            if (e.target?.result) {
              // 将图片数据转换为 base64 格式
              const base64Data = e.target.result as string
              // 创建一个 img 元素并插入到编辑器中setHtml
              const img = new Image()
              img.src = base64Data
              const imgHtml = `<img src="${base64Data}" alt="image" style="width:200px;height:200px;"/>`
              let editorRefs = wangEditorRef?.value.editorRef
              const editor = unref(editorRefs)
              if (editor) {
                // 通过 dangerouslyInsertHtml 插入图片的 HTML,控制宽高
                editor.dangerouslyInsertHtml(imgHtml)
              }
            }
          }
          reader.readAsDataURL(file)
        }
      }
    }
    // 返回值(注意,vue 事件的返回值,不能用 return)
    // callback(false) // 返回 false ,阻止默认粘贴行为
    callback(true) // 返回 true ,继续默认的粘贴行为
  }

  defineExpose({
    headerTitle,
  })
</script>

效果图


http://www.kler.cn/a/516964.html

相关文章:

  • centos7执行yum操作时报错Could not retrieve mirrorlist http://mirrorlist.centos.org解决
  • ES6语法
  • Day 15 卡玛笔记
  • Docker网段和服务器ip冲突导致无法访问网络的解决方法
  • 26. 【.NET 8 实战--孢子记账--从单体到微服务】--需求更新--用户注销、修改用户名、安全设置
  • 我的图形布局 组织结构图布局
  • 【含开题报告+文档+PPT+源码】基于Java Springboot的仓库管理系统设计与实现
  • 从 Facebook 的数据泄露事件看社交平台的隐私保护责任
  • Python基于Django的花卉商城系统的设计与实现(附源码,文档说明)
  • 图形化数据报文转换映射工具
  • HTTP 配置与应用(不同网段)
  • Lua语言的Web开发
  • MySQL创建和操纵表
  • 使用 GitHub Page 托管个人博客
  • GPU算力平台|在GPU算力平台部署可图大模型Kolors的应用实战教程
  • Oracle、PostgreSQL该学哪一个?
  • Stable Diffusion 秋叶整合包v4.7 :解压即用,快速入门AI绘画
  • Starrocks-数据备份与恢复
  • 【嵌入式】总结——Linux驱动开发(三)
  • 低代码系统-产品架构案例介绍,宜搭(五)
  • docker安装consul并启动的详细步骤
  • Redis高阶2-BigKey
  • Redis-HyperLogLog
  • React 19 新特性总结
  • Chrome 132 版本新特性
  • tomcat shutdown.sh不能关闭tomcat 进程