wangeditor5在vue3中的全使用过程(图片上传、附件上传、工具栏配置、编辑器配置)

news2025/2/25 17:09:23

1、参考官方的wangeditor5-for-vue3的开发手册

官方文档地址:https://clinfc.github.io/wangeditor5-for-vue3/guide/

说明为说明要编写这编博客文章?

官方文档的使用手册对于新手来说比较的难看懂,写的也不够详细,源码的封装比较深。写博客的目的是为了详细讲解一个适合项目使用的wangeditor的基本全过程,适合直接复制使用和修改(原官方文档使用原生js编写)

2、下载编辑器的依赖

npm install @wangeditor/editor --save
npm install @wangeditor/editor-for-vue@next --save

# 下面是vue3单独的组件,上面两个是旧的

npm install @wangeditor/editor wangeditor5-for-vue3

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4L9P2FUX-1663476493749)(image/1、wangeditor依赖.png)]

21.、新旧组件对比

下面的组件只是作为一个对比,不详细讲

旧组件

根据看与编辑器分开封装组件

<template>
    <div style="border: 1px solid #ccc">
      <Toolbar
        style="border-bottom: 1px solid #ccc"
        :editor="editorRef"
        :defaultConfig="toolbarConfig"
        :mode="mode"
      />
      <Editor
        style="height: 500px; overflow-y: hidden;"
        v-model="valueHtml"
        :defaultConfig="editorConfig"
        :mode="mode"
        @onCreated="handleCreated"
      />
    </div>
</template>

新组件

WeEditor 组件将 WeToolbarWeEditable 组件封装在了一个组件中,使用更方便。

<template>
  <we-editor
    toolbar-class="toolbar"
    editable-class="editable"
    toolbar-style="border: 1px solid #d9d9d9"
    editable-style="border: 1px solid #d9d9d9"
    :toolbar-option="toolbar"
    :editable-option="editable"
    :toolbar-reloadbefore="onToolbarReloadBefore"
    :editable-reloadbefore="onEditableReloadBefore"
    v-model="formData.jarr"
    v-model:json="formData.jstr"
    v-model:html="formData.html"
  />
</template>

3、入门使用<we-editor>组件

创建一个新的vue页面来编写当前demo

3.1、使用上面的新组件

3.2、编写js代码

<script>
// 引入 wangeditor5 样式
import '@wangeditor/editor/dist/css/style.css'

import { WeEditor, useWangEditor } from 'wangeditor5-for-vue3'
import { defineComponent, shallowReactive } from 'vue'
export default defineComponent( {
	name: "wangeditor",
	components: { WeEditor },
	setup() {
		// 编辑器配置
		const editableOption = {}

		// 菜单栏配置
		const toolbarOption = {}

		// 防抖时长。当会触发重载的配置项发生变化 365ms 后,编辑器会重载
		const reloadDelary = 365

		// 对于上面的三个对象,经过 useWangEditor 处理后,返回的 editable 和 toolbar 分别对应编辑器和菜单栏的配置项
		const { editable, toolbar } = useWangEditor(
			editableOption,
			toolbarOption,
			reloadDelary
		)

		// 开启只读模式【不可编辑】
		editable.config.readOnly = false

		// 不要使用 reactive/ref,应该使用 shallowReactive/shallowRef 来接收 json array 数据
		const formData = shallowReactive({ jarr: [], jstr: '', html: '' })

		// 在可编辑的重新加载之前
		function onEditableReloadBefore(inst) {
			console.log(inst)
			console.log('editable 即将重载: ' + new Date().toLocaleString())
		}

		// 在工具栏上重新加载之前
		function onToolbarReloadBefore(inst) {
			console.log(inst)
			console.log('toolbar 即将重载: ' + new Date().toLocaleString())
		}

		return { editable, toolbar, formData, onEditableReloadBefore, onToolbarReloadBefore }
	},
})
</script>

useWangEditor的官方说明

经过 useWangEditor 处理后,返回的 editabletoolbar 分别对应编辑器菜单栏的配置项,不过此时的配置项对象具备了响应式特性,我们可以直接修改 editable/toolbar 对应属性来更新重载编辑器。

如果传入的 editableOptiontoolbarOption 是响应式数据,内部将自动解除与之前的关联,也就意味着经过 useWangEditor 处理后得到的 editabletoolbar 配置对象,即使内容发生变化也不会触发之前的依赖更新!!!

大白话:可以在useWangEditor之后的对象中编写,也可以直接在editableOption对象中编写好再进过useWangEditor处理,不建议各自写一点,因为会覆盖,要么在前面写要么在后面写

3.3、编写样式

原来组件上面通过 toolbar-style="border: 1px solid #d9d9d9"editable-style="border: 1px solid #d9d9d9"来指定了工具栏和编辑器的样式边框,通过查看DOM元素赋值如下

<style>
/*工具栏样式*/
.toolbar{
	border: 1px solid #d9d9d9;margin-bottom: 10px;
}
/*工具栏剧中显示*/
.w-e-toolbar {
	justify-content: center !important;
}
/*编辑器样式*/
.editable{
	border: 1px solid #d9d9d9;
	min-height: 800px;
	width: 850px;
	margin: 30px auto 150px auto;
	background-color: #fff;
	box-shadow: 0 2px 10px rgb(0 0 0 / 12%);
	border: 1px solid #e8e8e8;
}
</style>

初始化效果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b3znbMhl-1663476493750)(image/2、编辑器效果.png)]

4、工具栏的修改(toolbarOption)

关于工具栏的排序以及菜单的配置的获取,根据官网的语句通过vue方法获取我暂时没弄懂,所以通过原生的方式获取

4.1、获取工具栏默认配置

进入官方提供的Demo示例,按F12输入如下命令查看工具栏的默认配置

toolbar.getConfig()

4.2、查看当前工具栏的默认配置

toolbar.getConfig().toolbarKeys

4.3、查询编辑器注册的所有菜单 key (可能有的不在工具栏上)

editor.getAllMenuKeys()

4.4、操作过程

官方demo地址:https://www.wangeditor.com/demo/index.html

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sQDyZiVO-1663476493751)(image/3、浏览器查看配置.png)]

4.5、工具栏模式对比及修改

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2bAWSN5C-1663476493751)(image/4、工具栏模式对比.png)]

方式一,直接在菜单栏配置对象里面写

// 菜单栏配置
const toolbarOption = {
    mode: 'simple' // 指定简介模式
}

方式二、通过useWangEditor转换后的toolbar进行重新

注意:后面的配置会覆盖前面的配置,所以当前设置的模式为default被后面所覆盖了

默认配置建议在toolbarOption写好在通过useWangEditor转换

// 对于上面的三个对象,经过 useWangEditor 处理后,返回的 editable 和 toolbar 分别对应编辑器和菜单栏的配置项
const { editable, toolbar } = useWangEditor(
    editableOption,
    toolbarOption,
    reloadDelary
)

toolbar.mode = 'default'

4.6、重新配置工具栏,显示哪些菜单,以及菜单的排序、分组

根据上面的4.2步骤获取到了当前默认的工具栏配置如下,与工具栏一一对应

[
    "headerSelect",
    "blockquote",
    "codeBlock",
    "|",
    "bold",
    "underline",
    "italic",
    {
        key: "group-more-style",
        title: "更多",
        iconSvg: "<svg viewBox=\"0 0 1024 1024\">" +
            "<path d=\"M204.8 505.6m-76.8 0a76.8 76.8 0 1 0 153.6 0 76.8 76.8 0 1 0-153.6 0Z\"></path>" +
            "<path d=\"M505.6 505.6m-76.8 0a76.8 76.8 0 1 0 153.6 0 76.8 76.8 0 1 0-153.6 0Z\"></path>" +
            "<path d=\"M806.4 505.6m-76.8 0a76.8 76.8 0 1 0 153.6 0 76.8 76.8 0 1 0-153.6 0Z\"></path>" +
            "</svg>",
        menuKeys: ["through", "code", "sup", "sub", "clearStyle"]
    },
    "color",
    "bgColor",
    "|",
    "fontSize",
    "fontFamily",
    "lineHeight",
    "|",
    "bulletedList",
    "numberedList",
    "todo",
    {
        key: "group-justify",
        iconSvg: "<svg viewBox=\"0 0 1024 1024\"><path d=\"M768 793.6v102.4H51.2v-102.4h716.8z m204.8-230.4v102.4H51.2v-102.4h921.6z m-204.8-230.4v102.4H51.2v-102.4h716.8zM972.8 102.4v102.4H51.2V102.4h921.6z\"></path></svg>",
        title: "对齐",
        menuKeys: ["justifyLeft", "justifyRight", "justifyCenter", "justifyJustify"]
    },
    {
        key: "group-indent",
        title: "缩进",
        iconSvg: "<svg viewBox=\"0 0 1024 1024\"><path d=\"M0 64h1024v128H0z m384 192h640v128H384z m0 192h640v128H384z m0 192h640v128H384zM0 832h1024v128H0z m0-128V320l256 192z\"></path></svg>",
        menuKeys: ["indent", "delIndent"]
    },
    "|",
    "emotion",
    "insertLink",
    {
        key: "group-image",
        title: "图片",
        iconSvg: "<svg viewBox=\"0 0 1024 1024\"><path d=\"M959.877 128l0.123 0.123v767.775l-0.123 0.122H64.102l-0.122-0.122V128.123l0.122-0.123h895.775zM960 64H64C28.795 64 0 92.795 0 128v768c0 35.205 28.795 64 64 64h896c35.205 0 64-28.795 64-64V128c0-35.205-28.795-64-64-64zM832 288.01c0 53.023-42.988 96.01-96.01 96.01s-96.01-42.987-96.01-96.01S682.967 192 735.99 192 832 234.988 832 288.01zM896 832H128V704l224.01-384 256 320h64l224.01-192z\"></path></svg>",
        menuKeys: ["insertImage", "uploadImage"]
    },
    {
        key: "group-video",
        title: "视频",
        iconSvg: "<svg viewBox=\"0 0 1024 1024\"><path d=\"M981.184 160.096C837.568 139.456 678.848 128 512 128S186.432 139.456 42.816 160.096C15.296 267.808 0 386.848 0 512s15.264 244.16 42.816 351.904C186.464 884.544 345.152 896 512 896s325.568-11.456 469.184-32.096C1008.704 756.192 1024 637.152 1024 512s-15.264-244.16-42.816-351.904zM384 704V320l320 192-320 192z\"></path></svg>",
        menuKeys: ["insertVideo", "uploadVideo"]
    },
    "insertTable",
    "divider",
    "|",
    "undo",
    "redo",
    "|",
    "fullScreen"
]

通过如下方法重新配置简介模式下的工具栏

注意:如果想在useWangEditor处理后的toolbar对象修改,必须要在toolbarOption对象里面先创建空的一个config配置空对象后面才能修改到(来源一个网友的说法)

// 菜单栏配置
const toolbarOption = {
    mode: 'simple', // 指定简介模式
    config:{
        toolbarKeys:[
            "fontSize",'header1', 'header2', 'header3','header4','|',
            'blockquote',"code","codeBlock",'|',
            'bold', 'underline', 'italic', 'through', 'color', 'bgColor', 'clearStyle', '|',
            'bulletedList', 'numberedList', 'todo', 'justifyLeft','justifyCenter', 'justifyRight', '|',
            'insertLink',
            {
                key: 'group-image',
                title: '图片',
                iconSvg: "<svg viewBox=\"0 0 1024 1024\"><path d=\"M959.877 128l0.123 0.123v767.775l-0.123 0.122H64.102l-0.122-0.122V128.123l0.122-0.123h895.775zM960 64H64C28.795 64 0 92.795 0 128v768c0 35.205 28.795 64 64 64h896c35.205 0 64-28.795 64-64V128c0-35.205-28.795-64-64-64zM832 288.01c0 53.023-42.988 96.01-96.01 96.01s-96.01-42.987-96.01-96.01S682.967 192 735.99 192 832 234.988 832 288.01zM896 832H128V704l224.01-384 256 320h64l224.01-192z\"></path></svg>",
                menuKeys: ['insertImage', 'uploadImage']
            },
            "insertTable",
            "|",
            "undo","redo"
        ]
    }
}

简洁效果如下

后面再加入个附件上传的插件菜单

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EB3KioWj-1663476493752)(image/5、简洁排序.png)]

5、菜单配置(editableOption)

根据官网描述:wangEditor 从 V5 版本开始,工具栏配置和菜单配置(如配置颜色、字体、链接校验、上传图片等)分离了

菜单配置是什么?就是上图中的选择默认字体大小类的内容,也就是工具栏的菜单功能实现

5.1、查看编辑器配置

可通过 editor.getConfig() 查看编辑器默认配置

根据下图观察,editor后面使用的是getConfig是一个配置,所以下面的配置页要写在config对象里面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2BL4YauT-1663476493752)(image/6、查看编辑器配置.png)]

在4的步骤中使用editor.getAllMenuKeys()以及获取到了所有的菜单key,就可以进行下一步的菜单配置了

5.2、配置默认字号

如何查看keys和配置项?通过上图就可以看出对应的key,找到fontSize,点击进去就有fontSizeList数组了,根据里面的写法进行编写即可

// 编辑器配置
const editableOption = {
    config:{
        MENU_CONF:{
            fontSize:{
                fontSizeList: [
                    // 元素支持两种形式
                    //   1. 字符串;
                    //   2. { name: 'xxx', value: 'xxx' }
                    '16px',
                    '20px',
                    { name: '26px', value: '26px' },
                    '40px',
                ]
            }
        }
    }
}

效果如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UvzFqXw0-1663476493753)(image/7、修改默认子号.png)]

5.3、图片上传配置

根据查询的ket查看如何配置

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tp0SN8B9-1663476493753)(image/8、查看上传图片配置.png)]

实现

我这里引入了ElementUI的插件所以可以使用this.$message.success(${file.name} 上传成功, res)作为提示

后台的上传地址server需要自己编写能实现上传文件再进行测试

官方上传文件的返回类型

// 成功返回
{
 "errno": 0, // 注意:值是数字,不能是字符串
 "data": {
     "url": "xxx", // 图片 src ,必须
     "alt": "yyy", // 图片描述文字,非必须
     "href": "zzz" // 图片的链接,非必须
 }
}

// 失败返回
{
 "errno": 1, // 只要不等于 0 就行
 "message": "失败信息"
}

我的是通过customInsert自定义返回类型

// 编辑器配置
const editableOption = {
    config:{
        placeholder:"请在这里输入内容...",
        MENU_CONF:{
            // 配置默认字号
            // 配置上传图片
            uploadImage:{
                // 请求路径
                server: "api/sysUser/uploadImg",
                // 后端接收的文件名称
                fieldName: "file",
                maxFileSize: 1 * 1024 * 1024, // 1M
                // 上传的图片类型
                allowedFileTypes: ["image/*"],
                // 小于该值就插入 base64 格式(而不上传),默认为 0
                base64LimitSize: 10 * 1024, // 10MB
                // 自定义插入返回格式【后端返回的格式】
                customInsert(res, insertFn) {
                    if(res.code != 200 && res.success == false){
                        ElMessage.error("上传文件失败,"+res.message)
                        return
                    }
                    insertFn(res.data.url, res.data.alt, res.data.href)
                },
                // 携带的数据
                meta: {
                    token: 'eyJhbGciOiJIUzUxMiJ9.eyJleHAiOjE2NjM0MjQ5MzEsInN1YiI6ImFkbWluIiwiaWF0IjoxNjYzNDIzMTMxOTAyfQ.McM6MZ6N9dQnAKym-9_TqAv6gjRWqf72Q4MTnMlS9AeIM-DhCjaJJrUMYbB8hs5r-HXYSXbs5O5pk9f_KUbGQg'
                },
                // 将 meta 拼接到 url 参数中,默认 false
                metaWithUrl: true,
                // 单个文件上传成功之后
                onSuccess(file, res) {
                    if(res.code == 200 && res.success){
                        ElMessage.success(`${file.name} 上传成功`)
                        return
                    }else {
                        ElMessage.warning(`${file.name} 上传出了点异常`)
                        return
                    }
                    // console.log(`${file.name} 上传成功`, res)
                    //ElMessage.success(`${file.name} 上传成功`, res)
                },
                // 单个文件上传失败
                onFailed(file, res) {
                    console.log(res)
                    ElMessage.error(`${file.name} 上传失败`)
                },
                // 上传错误,或者触发 timeout 超时
                onError(file, err, res) {
                    console.log(err, res)
                    ElMessage.error(`${file.name} 上传出错`)
                },
            }
        }
    }
}

测试结果

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hh8k4mhB-1663476493754)(image/9、图片上传.png)]

5.4、视频上传

目前没有搞过,以后研究

6、上传附件插件使用

官方插件地址:https://github.com/wangeditor-team/wangEditor-plugin-upload-attachment

6.1、下载依赖

  1. 安装yarn

    npm install --global yarn
    # 检查是否成功
    yarn --version
    
  2. 安装插件依赖

    这是官网指定的,里面同时也包含了wangeditor5的全套依赖,使用的是yarn进行管理

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cMCaKgfA-1663476493755)(image/10、yarn'.png)]

    yarn add @wangeditor/plugin-upload-attachment
    

    跟着上面步骤走的就通过下面的方式获取到(原先已近下载了其他依赖)

    npm i @wangeditor/plugin-upload-attachment -s
    
  3. 查看

    在这里插入图片描述

6.2、【注意】该文档要求 @wangeditor/editor 版本 >=5.1.16

6.3、注册到编辑器

import { Boot } from '@wangeditor/editor'
import attachmentModule from '@wangeditor/plugin-upload-attachment'
import { WeEditor, useWangEditor } from 'wangeditor5-for-vue3'

// 注册。要在创建编辑器之前注册,且只能注册一次,不可重复注册。
Boot.registerModule(attachmentModule)

在app.vue里面就能实现一次的注册

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-p7bSA52j-1663476493755)(image/14、注册按钮.png)]

6.4、编辑器配置上传附件菜单

注意:如果在useWangEditor处理后的editable使用也需要在editableOptionconfig.MENU_CONF.uploadAttachment的空对象

我是一类ElementUI的消息提示组件,如果复制那一段可能编译报错,自己修改

// 编辑器配置
const editableOption = {
  config:{
    hoverbarKeys: {
      attachment: {
        menuKeys: ['downloadAttachment'], // “下载附件”菜单
      },
    },
    MENU_CONF: {
      	// 上传附件
        uploadAttachment:{
            server: 'http://localhost:8081/api/sysUser/blogFileUpload',
            fieldName: 'file',
            maxFileSize: 100 * 1024 * 1024, // 100M
            // 携带的数据
            meta: {
                token: 'eyJhbGciOiJIUzUxMiJ9.eyJleHAiOjE2NjM0MTQ1MjAsInN1YiI6ImFkbWluIiwiaWF0IjoxNjYzNDEyNzIwMDE5fQ.nFAEtMqduzValgDAsMUXeM0OSIlYK4hi8cjTSAL52jMgFwfuOUVNEbdT91abs1rdNXKjEtrymbn_2aO1Q2h26Q'
            },
            // 在上传之前
            onBeforeUpload(file) {
                console.log('onBeforeUpload', file)
                return file // 上传 file 文件
                // return false // 会阻止上传
            },
            // 关于进展
            onProgress(progress) {
                console.log('onProgress', progress)
            },
            // 成功回调
            onSuccess(file, res) {
                if(res.errno === 0){
                    ElMessage.success(`${file}附件上传成功`)
                }
            },
            // 失败回调
            onFailed(file, res) {
                if(res.errno === 1){
                    ElMessage.success(`${file}附件上传失败,`+res.message)
                }
            },
            // 错误
            onError(file, err, res) {
                console.error('onError', file, err, res)
            },

            // // 上传成功后,用户自定义插入文件
            // customInsert(res: any, file: File, insertFn: Function) {
            //   console.log('customInsert', res)
            //   const { url } = res.data || {}
            //   if (!url) throw new Error(`url is empty`)

            //   // 插入附件到编辑器
            //   insertFn(`customInsert-${file.name}`, url)
            // },

            // // 用户自定义上传
            // customUpload(file: File, insertFn: Function) {
            //   console.log('customUpload', file)

            //   return new Promise(resolve => {
            //     // 插入一个文件,模拟异步
            //     setTimeout(() => {
            //       const src = `https://www.w3school.com.cn/i/movie.ogg`
            //       insertFn(`customUpload-${file.name}`, src)
            //       resolve('ok')
            //     }, 500)
            //   })
            // },

            // // 自定义选择
            // customBrowseAndUpload(insertFn: Function) {
            //   alert('自定义选择文件,如弹出图床')
            //   // 自己上传文件
            //   // 上传之后用 insertFn(fileName, link) 插入到编辑器
            // },
            // 插入到编辑器后的回调
            onInsertedAttachment(elem) {
                console.log(elem)
            }
        },
    }
  }
}

6.5、工具栏插件附件按钮

方式一

const toolbarOption = {
    config:{
        // 插入哪些菜单
        insertKeys: {
            index: 0, // 自定义插入的位置
            keys: ['uploadAttachment'], // “上传附件”菜单
        },
    }
}

方式二

uploadAttachment就是附件的key

const toolbarOption = {
    config:{
        // 插入哪些菜单
        toolbarKeys:[
        	"fontSize",'header1', 'header2', 'header3','header4','|',
			'blockquote',"code","codeBlock",'uploadAttachment','|',
			....
        ]
    }
}

6.6、后端数据响应格式

成功

{
  "errno": 0,
  "data": {
    "url": "附件的下载链接"
  }
}

失败

{
  "errno": 1,
  "message": "错误信息"
}

编辑器响应插入的标签

<a data-w-e-type="attachment" data-w-e-is-void data-w-e-is-inline href="https://xxx.com/aaa/bbb/xxx.zip" download="xxx.zip">xxx.zip</a>

6.7、上传附件测试

需要自己编辑后端接口,并启动按照响应格式返回(文件会直接下载,图片打开一个新的窗口查看,再自行按需保存)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dEuVFFKc-1663476493756)(image/13、上传附件.png)]

点击附件出现内容的关键是

hoverbarKeys: {
  attachment: {
    menuKeys: ['downloadAttachment'], // “下载附件”菜单
  },
},

7、模型双向绑定

WeEditable/WeEditor/WeEditorPlus 组件同时支持 v-modelv-model:jsonv-model:html 三种形式的双向绑定,分别对应 json arrayjson stringhtml string 三种格式的数据。

注意事项:

  • 注意 WeEditableOption.extendCache 可能存在的影响!!!
  • 当我们进行 v-model 绑定时,推荐使用 shallowReactive/shallowRef 来缓存 json array 数据。如果你执意使用 reactive/ref 进行数据缓存,那么在出现未知错误时你可能找不到问题所在。
  • 在提交表单前,或手动触发表单验证前,请使用 syncContent 来强制同步 v-model 数据,避免数据不一致。
  • 双向绑定多个同时使用时,存在 v-model > v-model:json > v-model:html 的优先级关系。即:如果你使用优先级低的来设置数据的话,设置将被拦截(设置无效)。

最优搭配为 v-html:jsonv-model:json + v-model:htmlv-model:json 相对 v-model 而言,能减少大量内存消耗和计算消耗。

7.1、获取数据

useWangEditor处理后的时候一个syncContent,才能使获取到的数据同步,返回submit方法,给按钮绑定点击事件调用即可

setup() {
	....
    // 对于上面的三个对象,经过 useWangEditor 处理后,返回的 editable 和 toolbar 分别对应编辑器和菜单栏的配置项
    const { editable, toolbar ,syncContent } = useWangEditor(
        editableOption,
        toolbarOption,
        reloadDelary,
        {
            delay: 3000, // 无操作 3s 后才会同步表单数据
            config: {
                placeholder: '表单提交前使用 syncContent API 强制同步数据,确保数据不被丢失',
            },
        }
    )
	// 获取数据
    const formData = shallowReactive({ html: '' })
    ....
	// 表单提交
    function submit() {
        // 强制同步 v-model 数据
        syncContent()
        // 表单验证
        if(formData.html!=''){
            // TODO 进行业务处理
            ElMessage.success(formData.html)
        }else {
            ElMessage.error("请在编辑器内编写内容...")
        }
    }

    return { editable, toolbar, formData, submit,onEditableReloadBefore, onToolbarReloadBefore }
}

获取到的数据后就可以在仅一步处理数据编写正常的业务流程了

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/408205.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vue3+vite项目配置ESlint、pritter插件

前言 入行前端工作将近两年多时间了&#xff08;如果算上实习&#xff09;&#xff0c;从一开始vue2入门&#xff0c;到现在vue3前端变化是真的快&#xff0c;刚了解webpack搭建项目流程&#xff0c;vite又横空出世&#xff0c;不得不说前端变化真的太快了&#xff0c;所以只有…

【JavaScript】DOM和事件简介和文档加载流程以及DOM查询(上)案例(附源码)

&#x1f41a;作者简介&#xff1a;苏凉&#xff08;专注于网络爬虫&#xff0c;数据分析&#xff0c;正在学习前端的路上&#xff09; &#x1f433;博客主页&#xff1a;苏凉.py的博客 &#x1f310;系列总专栏&#xff1a;web前端基础教程 &#x1f451;名言警句&#xff1a…

自己的智能AI聊天机器人,可自定义头像,免费html源码分享,粘贴即用!

1.展示效果效果预览图&#xff1a;新增小功能&#xff1a;① 在原有的基础上加入了本地实时存档的功能&#xff0c;按照下面的步骤便可以随时在本地查看以往和智能AI所有的聊天记录哦&#xff01;再也不用担心关闭网页后先前的聊天内容全部消失啦&#xff01;PS&#xff1a;最新…

解决宝塔 Nginx 跨域问题Access-Control-Allow-Origin

何为跨域&#xff1f; 1、资源跳转&#xff1a; A链接、重定向、表单提交 2.资源嵌入&#xff1a; <link>、<script>、<img>、<frame>等dom标签&#xff0c;还有样式中background:url()、font-face()等文件外链 3.脚本请求&#xff1a; js发起的ajax请…

商城系统需求分析

文章目录一、引言1.1项目背景1.2 前期工作二、技术概述三、功能需求3.1 功能块划分3.2 功能块描述3.2.1 面向用户部分功能&#xff1a;3.2.2 后台管理部分功能&#xff1a;四、性能需求4.1 数据精确度4.2 适应性五、系统流程图5.1 顾客与管理员流程图如下5.2 订单处理流程说明六…

VUE动态切换皮肤 VUE动态切换背景图片 操作 / VUE 主题切换

上正文 使用&#xff1a;root &#xff0c;var&#xff08;&#xff09;函数实现 1. 创建皮肤或主题 css目录 一个公共主题文件 theme.css&#xff0c;一个main.js引入文件theme-all.css&#xff0c;一个单独的 主题样式文件 theme-12.css 2. 定义css文件中所要切换的主题的…

vue(绑定style属性)

以对象方式绑定style属性 <div id"app"> <!-- 在行内属性中书写样式 --> <div style"color:royalblue ; font-size: 48px;">黄绥睿真帅个鬼</div> <!-- 把行内属性改造成对象&#xff0c;以对象方式绑定style属性 外部增加{}&a…

【进阶】TS 中的 类型断言 和 泛型

类型断言 作用 : 手动指定值的具体类型 ( 缩写值的范围 ) 应用场景 1 获取 DOM 元素的时候指定具体元素 示例 : const box document.getElementById(img) console.log(box.src) // ts报错错误内容 : 解析 &#xff1a; 上述语法在 js 中可以直接使用, 但是 TS 中就不行…

vue 动态样式绑定 class/style

简介&#xff1a; 字符串写法&#xff1a;类名不确定&#xff0c;要动态获取 对象写法&#xff1a;要绑定多个样式&#xff0c;个数确定&#xff0c;名字确定&#xff0c;但不确定用不用。 数组写法&#xff1a;要绑定多个样式&#xff0c;个数不确定&#xff0c;名字不确定。…

HTML基础 - HTML表格

HTML基础 - HTML表格 1.无表头的表格 <table> <tr> <td> <table>标签代表的是表 <tr>标签代表的是行 <td>标签代表的是列 在html页面中的表格来着&#xff0c;就和excl的表格不一样喽&#xff0c;咱自己有自己的规则&#xff1a; 这就是…

这一次,彻底搞懂箭头函数

一、箭头函数的特点 1. 相比普通函数&#xff0c;箭头函数有更加简洁的语法。 普通函数 function add(num) {return num 10 }箭头函数 const add num > num 10;2. 箭头函数不绑定this&#xff0c;会捕获其所在上下文的this&#xff0c;作为自己的this。 这句话需要注意的…

若依(ruoyi)字典管理插件实现思路探究

一个UI表单的构成&#xff0c;避免不了下拉框&#xff0c;多选框等标签&#xff0c;在开发这些标签时&#xff0c;通常会请求后台接口获取字典值进行动态渲染。定制化开发虽然实现简单&#xff0c;但会产生大量重复工作&#xff0c;解决这类问题的思路有哪些&#xff1f;文章对…

chrome插件开发时跨域问题解决方案

这是一个没有套路的前端博主&#xff0c;热衷各种前端向的骚操作&#xff0c;经常想到哪就写到哪&#xff0c;如果有感兴趣的技术和前端效果可以留言&#xff5e;博主看到后会去代替大家踩坑的&#xff5e;接下来的几篇都是uni-app的小实战&#xff0c;有助于我们更好的去学习u…

Vue在HTML中如何使用

&#x1f440;Vue是什么 一套用于构建用户界面的渐进式JavaScript框架。 构建用户界面&#xff1a;数据变成界面渐进式&#xff1a;Vue可以自底向上逐层的应用&#x1f440;Vue如何使用 一、引入vue.js <script src"./js/vue.js"></script> 二、准备一个…

HBuilderX uni-app简单实现静态登录页面(实例)

本章用到......uni-app页面跳转uni.navigateTo方法、uni.navigateBack方法。uni-app简单实现邮箱验证码发送点击后读秒样式。登录账号、密码正则表达式验证等 适合刚入门的小伙伴&#xff0c;大佬就没必要看了 静态页面&#xff01;静态页面&#xff01;没有绑定后端数据接口…

拿来即用的前端登录页面(简洁清爽版)

1、使用bootstrap实现 代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>登录</title><link rel"stylesheet" href"/bootstrap-3.3.7-dist/css/bootstrap.m…

vue项目根据不同环境动态配置接口请求ip及全局变量(vue环境变量配置)

在项目的开发过程中&#xff0c;我们常常会遇到根据不同的环境需要切换不同的ip的问题&#xff0c;例如在项目部署到测试服时需要将接口请求ip替换成测试服的ip,部署到正式服时又需要将接口请求ip替换成正式服对应的ip,有些公司还有预发环境等&#xff0c;这样在每次部署不同环…

Vue实战篇三十五:实现滑动拼图验证登录

系列文章目录 Vue基础篇一&#xff1a;编写第一个Vue程序 Vue基础篇二&#xff1a;Vue组件的核心概念 Vue基础篇三&#xff1a;Vue的计算属性与侦听器 Vue基础篇四&#xff1a;Vue的生命周期&#xff08;秒杀案例实战&#xff09; Vue基础篇五&#xff1a;Vue的指令 Vue基础篇…

Please set spring.main.web-application-type=reactive or remove spring-boot-starter-web dependency.

一、问题在启动springcloud的gateway模块的时候报错Please set spring.main.web-application-typereactive or remove spring-boot-starter-web dependency.二、问题产生的原因gateway组件中的 spring-boot-starter-webflux 和 springboot作为web项目启动必不可少的 spring-boo…

前端如何将静态页面部署到服务器,并可以通过公网ip访问。

问题描述 作为卑微的前端页面仔。在我们公司项目上线的时候&#xff0c;一般都是我们前端 npm run build&#xff0c;然后直接把打出来的dist包丢给后端&#xff0c;后端上传到服务器完成前端的部署。这个时候我就很好奇&#xff0c;页面是怎么上传到服务器的呢&#xff1f;上…