• vue3 +ts 项目 实现富文本编辑 获取数据,数据回显


    需求:
    描述栏 使用富文本编辑器 可以文本操作,可以图片,可以表格,超链接等
    实现效果:
    在这里插入图片描述
    代码:

    1. 新建文件夹 /components/ToastuiEditor/index.vue 文件

    封装的富文本内容 组件。(代码见第四条)

    2. 引入插件

    npm install --save @toast-ui/editor
    npm i process
    // 强大的异步专家process.nextTick()
    
    @toast-ui/editor-plugin-chart:图表插件 
    @toast-ui/editor-plugin-code-syntax-highlight:高亮插件 
    @toast-ui/editor-plugin-color-syntax:颜色选择插件
    @toast-ui/editor-plugin-table-merged-cell:
    表格合并行列插件 @toast-ui/editor-plugin-uml:uml插件
    

    3.使用 :

    <template>
      <div>
        <el-form :model="formDialogCommon" class="demo-form-top">
            <el-form-item label="描述:">
                <toastui-editor
                    ref="refToastuiEditor"
                    id="ToastuiEditor1"
                    style="width: 100%"
                ></toastui-editor>
            </el-form-item>
        </el-form>
            <div class="dialog-from-button">
                <el-button size="small" type="info">取消</el-button>
                <el-button type="primary" size="small" @click="fromSubmit()"
                >确认</el-button>
            </div>
      </div>
    </template>
    
    <script lang="ts">
    import {
      defineComponent,
      reactive,
      ref
    } from 'vue'
    //引入封装的组件
    import ToastuiEditor from '@/components/ToastuiEditor/index.vue'
    
    export default defineComponent({
      name: 'App',
      components: { ToastuiEditor },
      setup() {
        const formDialogCommon = reactive({
          richText: '',
        })
        const refToastuiEditor = ref()
        // 编辑
        const compileClick = async(v: any) => {
            if (v.showText) {
              refToastuiEditor.value.setHTML(v.showText)
              //用于数据回显 (v.showText表示需要回显的数据)
            }
        }
        // 确认
        const fromSubmit = () => {
            const richTextHtml: string = refToastuiEditor.value.getHTML()
            formDialogCommon.richText = richTextHtml
            // 获取到数据 formDialogCommon.richText 用于传递给接口
        }
        return {
          refToastuiEditor,
          fromSubmit,
          compileClick
        }
      }
    })
    </script>
    

    4. 封装

    //components/ToastuiEditor/index.vue 文件
    <template>
      <div v-if="id" :id="id"></div>
    </template>
    
    <script lang="ts">
    import '@toast-ui/editor/dist/i18n/zh-cn.js' // 工具栏显示中文
    import '@toast-ui/editor/dist/toastui-editor.css'
    import Editor, { EditorCore } from '@toast-ui/editor'
    import { reactive } from '@vue/reactivity'
    import { defineComponent, onMounted, ref } from 'vue'
    import { nextTick } from 'process'
    import { ElMessage } from 'element-plus'
    export default defineComponent({
      name: 'ToastuiEditor',
      props: ['id', 'height', 'initialEditType'],
      setup(props) {
        const editorText = ref('This is initialValue.')
        const editorOptions = reactive({
          hideModeSwitch: true
        })
        // 压缩方法
        const dealImage = (base64: string, w: number, callback: { (arg2: any, e: any): void; (arg0: string): void }) => {
          const newImage = new Image()
          // 压缩系数0-1之间
          let quality = 0.6
          newImage.src = base64
          newImage.setAttribute('crossOrigin', 'Anonymous') // url为外域时需要
          let imgWidth, imgHeight
          newImage.onload = function() {
            imgWidth = window.innerWidth
            imgHeight = window.innerHeight
            const canvas = document.createElement('canvas')
            const ctx = canvas.getContext('2d')
            if (Math.max(imgWidth, imgHeight) > w) {
              if (imgWidth > imgHeight) {
                canvas.width = w
                canvas.height = (w * imgHeight) / imgWidth
              } else {
                canvas.height = w
                canvas.width = (w * imgWidth) / imgHeight
              }
            } else {
              canvas.width = imgWidth
              canvas.height = imgHeight
              quality = 0.6
            }
            ctx?.clearRect(0, 0, canvas.width, canvas.height)
            ctx?.drawImage(newImage, 0, 0, canvas.width, canvas.height)
            // ctx?.drawImage(this:GlobalEventHandlers, 0, 0, canvas.width, canvas.height)
            const ba = canvas.toDataURL('image/jpeg', quality) // 压缩语句
            // 如想确保图片压缩到自己想要的尺寸,如要求在50-150kb之间,请加以下语句,quality初始值根据情况自定
            while (base64.length / 1024 > 150) {
              quality -= 0.01
              base64 = canvas.toDataURL('image/jpeg', quality)
            }
            // 防止最后一次压缩低于最低尺寸,只要quality递减合理,无需考虑
            while (base64.length / 1024 < 50) {
              quality += 0.001
              base64 = canvas.toDataURL('image/jpeg', quality)
            }
            callback(ba) // 必须通过回调函数返回,否则无法及时拿到该值
          }
        }
        const fileOrBlobToDataURL = (obj: Blob, cb: { (dataurl: string, obj: { size: number }): void; (arg0: any, arg1: any): void }) => {
          const a: FileReader = new FileReader()
    
          a.readAsDataURL(obj)
          a.onload = (e: any) => {
            cb(e.target.result, obj)
          }
        }
        const blobToImage = (blob: any, callback: (arg0: string) => void) => {
          return fileOrBlobToDataURL(blob, function(dataurl: string, obj: { size: number }) {
            // const img = new Image();
    
            // 获取大小,大于2兆缩小操作
            // console.log((obj.size / 1048576).toFixed(2))
            if (Number((obj.size / 1048576).toFixed(2)) > 2) {
              dealImage(dataurl, 1200, (e: string) => {
                dataurl = e
                callback(dataurl)
              })
            } else {
              callback(dataurl)
            }
          })
        }
        let currentEditor: Editor = null as unknown as Editor
        const setHTML = (value: string) => {
          if (currentEditor) {
            currentEditor.setHTML(value)
          }
        }
        const getHTML = () => {
          if (currentEditor) {
            return currentEditor.getHTML()
          }
        }
        onMounted(() => {
          nextTick(() => {
            currentEditor = new Editor({
              language: 'zh-CN',
              el: document.querySelector('#' + props.id) as HTMLElement,
              height: props.height ? props.height : '400px',
              initialEditType: props.initialEditType
                ? props.initialEditType
                : 'wysiwyg',
              hooks: {
                addImageBlobHook: (blob: any, callback: (arg0: string) => void) => {
                  // 查询有现有文本含有多少张图片
                  const re = /]+src="[^"]+"[^>]*>/gim // 创建正则表达式模式。; // 声明变量。
                  const text = currentEditor!.getHTML()
                  const r = text.match(re) || [] // 尝试去匹配搜索字符串。
                  if (r.length >= 10) {
                    ElMessage({
                      message: '最多十张',
                      type: 'warning'
                    })
                  } else {
                    blobToImage(blob, callback)
                  }
                }
              }
            }) as Editor
          })
        })
        return {
          editorText,
          editorOptions,
          currentEditor,
          setHTML,
          getHTML
        }
      }
    })
    </script>
    
  • 相关阅读:
    SpringBoot配置文件的加载顺序
    K8S之Deployment控制pod
    python超详细安装
    如何使用 ArcGIS Pro 统计四川省各市道路长度
    vue3 setup实现响应式
    IDM安装教程
    python个人博客毕业设计开题报告
    promise执行顺序面试题令我头秃,你能作对几道
    数据分享|Python卷积神经网络CNN身份识别图像处理在疫情防控下口罩识别、人脸识别
    小满网络模型&http1-http2 &浏览器缓存
  • 原文地址:https://blog.csdn.net/weixin_55042716/article/details/127105497