WangEdtior富文本编辑器

WangEdtior官网,提供了vue2、vue3的安装方式>>

原生js使用

WangEdtior富文本编辑器_第1张图片

DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Documenttitle>

    <link href="https://unpkg.com/@wangeditor/editor@latest/dist/css/style.css" rel="stylesheet">
    <script src="https://unpkg.com/@wangeditor/editor@latest/dist/index.js">script>

    <style>
        #editor—wrapper {
            border: 1px solid #ccc;
            z-index: 100;
            /* 按需定义 */
            width: 800px;
            margin: 10px auto;
        }


        #toolbar-container {
            border-bottom: 1px solid #ccc;
        }

        #editor-container {
            height: 150px;
        }

        p {
            /* 将p的margin改为0, 不然会出现改了#editor-container的高度,编辑器已经足够放下内容了,但是仍然会有滚动条出现的问题 */
            margin: 0 !important;
            line-height: 1.3em;
        }

        .w-e-scroll>div:first-child {
            padding-top: 15px; /* 将输入起始位置与“请输入内容对齐” */
        }
    style>
head>

<body>

    <div id="editor—wrapper">
        
        <div id="toolbar-container">div>
        
        <div id="editor-container">div>
    div>

body>
<script>

    const { createEditor, createToolbar } = window.wangEditor

    /* 编辑器配置 */
    const editorConfig = {
        placeholder: '请输入内容...',
        onChange(editor) {
            const html = editor.getHtml()
            console.log('editor content', html)
        }
    }

    /* 编辑器 */
    const editor = createEditor({
        selector: '#editor-container',
        html: '


'
, config: editorConfig, mode: 'default', // or 'simple' }) /* 工具栏配置 */ const toolbarConfig = {} /* 工具栏 */ const toolbar = createToolbar({ editor, selector: '#toolbar-container', config: toolbarConfig, mode: 'default', // or 'simple' })
script> html>

vue2

安装wangEditor

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

ArticleWang.vue

WangEdtior富文本编辑器_第2张图片

<style>
p {
    /* 将p的margin改为0, 不然会出现改了#editor-container的高度,编辑器已经足够放下内容了,但是仍然会有滚动条出现的问题 */
    margin: 0 !important;
    line-height: 1.3em;
}

.w-e-scroll>div:first-child {
    padding-top: 15px;
    /* 将输入起始位置与“请输入内容对齐” */
}
style>
<template>
    <div style="border: 1px solid red;padding: 10px;">

        
        <div style="text-align: center;">
            <el-button type="success" plain @click="getHtml">getHtmlel-button>
            <el-button type="success" plain @click="setHtml">setHtmlel-button>
            <el-button type="success" plain @click="resetHtml">resetHtmlel-button>
            <el-button type="success" plain @click="getText">getTextel-button>
            <el-button type="success" plain @click="focus">focusel-button>
            <el-button type="success" plain @click="saveHtml">saveHtmlel-button>
        div>
        <div style="text-align: center;margin-top: 10px;">
            <el-button type="success" plain @click="playToolbar">playToolbarel-button>
            <el-button type="success" plain @click="enableEdit">enableEditel-button>
        div>

        <div style="text-align: center;margin-top: 10px;">
            <el-button type="success" plain @click="getUploadImageMenuConfig">getUploadImageMenuConfigel-button>
        div>

        
        <div style="border: 1px solid #ccc; width: 800px;margin: 10px auto 0;">

            
            <Toolbar style="border-bottom: 1px solid #ccc" :editor="editor" :defaultConfig="toolbarConfig" :mode="mode" />

            
            <Editor style="height: 310px;" v-model="htmlContent" :defaultConfig="editorConfig" :mode="mode"
                @onCreated="onCreated" @onChange="onChange"/>
        div>

        <div style="width: 800px;margin: 10px auto;" v-html="htmlContent">div>
    div>
template>

<script>

import { Editor, Toolbar } from '@wangeditor/editor-for-vue'

/* 可以用来获取toolbar */
import { DomEditor } from '@wangeditor/editor'

/* 引入wangEditor的样式 */
import '@wangeditor/editor/dist/css/style.css'

import axiosInstance from '@/utils/request'

export default {
    name: 'ArticleWang',
    components: {
        Editor, Toolbar
    },
    data() {
        return {
            editor: null,                 /* 编辑器实例,在编辑器创建完成时,赋值 */
            htmlContent: '

hello

'
, /* 编辑器内容 */ toolbarConfig: { /* 工具栏配置 */ excludeKeys: [ 'group-video' // 排除视频这个菜单 ] }, editorConfig: { /* 编辑器配置 */ placeholder: '请输入内容...', // maxLength: 50, // 限制长度50 MENU_CONF: { emotion: { // 表情菜单 emotions: ' '.split(' ') // 数组 }, uploadImage: { // 图片上传 server: 'http://localhost:8083/article/uploadImg', /* 上传接口 */ fieldName: 'mfile', /* 文件参数名 */ "allowedFileTypes": [ /* 允许的文件类型 */ "image/*" ], maxFileSize: 10 * 1024 * 1024, /* 单个文件的最大体积限制,默认为 2M */ maxNumberOfFiles: 10, /* 最多可上传几个文件,默认为 100 */ // 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。 meta: { token: 'xxx', otherKey: 'yyy' }, // 将 meta 拼接到 url 参数中,默认 false metaWithUrl: false, // 自定义增加 http header headers: { Accept: 'application/json;charset=utf-8', otherKey: 'xxx' }, // 跨域是否传递 cookie ,默认为 false withCredentials: true, // 超时时间,默认为 10 秒 timeout: 5 * 1000, // 5 秒 // 自定义插入图片 /* 上传成功的返回格式:{ "errno": 0, // 注意:值是数字,不能是字符串 "data": { "url": "xxx", // 图片 src ,必须 "alt": "yyy", // 图片描述文字,非必须 "href": "zzz" // 图片的链接,非必须 } } 上传失败的返回格式:{ "errno": 1, // 只要不等于 0 就行 "message": "失败信息" } 如果你的服务端 response body 无法按照上文规定的格式,则无法插入图片,提示失败。 但你可以使用 customInsert 来自定义插入图片) */ /* customInsert(res, insertFn) { // 从 res 中找到 url alt href ,然后插入图片 // insertFn(url, alt, href) insertFn(`http://localhost:8083/img/${res.data}`) }, */ /* 自己实现文件上传 */ customUpload(file, insertFn) { // file 即选中的文件 // 自己实现上传,并得到图片 url alt href // 最后插入图片 // 拿到 files 之后上传到文件服务器,然后向编辑框中插入对应的内容 let formData = new FormData() formData.append('mfile', file) axiosInstance({ url: "http://127.0.0.1:8083/article/uploadImg", method: 'POST', data: formData, headers: { 'Content-Type': 'multipart/form-data' } }).then(res => { insertFn('http://127.0.0.1:8083/img/' + res, res) console.log('插入成功'); }) }, }, insertImage: { /* 插入过的图片都会回调这个方法,最后保存富文本时,比较插入的记录和最终获取的image(editor.getElemsByType('image')),就能知道该删除哪些图片 */ onInsertedImage(imageNode) { console.log('onInsertedImage->imageNode',imageNode); } } } }, mode: 'default', editorEnabled: true, } }, methods: { onCreated(editor) {/* 编辑器创建完毕时的回调函数。 */ this.editor = Object.seal(editor) // 一定要用 Object.seal() ,否则会报错 }, onChange(editor) {/* 编辑器内容、选区变化时的回调函数 */ console.log('有人在动编辑器'); }, getHtml() { console.log(this.editor.getHtml()); }, setHtml() { /* 这里设置的内容只能是从wangEditor中获取的内容哦 */ this.editor.setHtml('

模拟 Ajax 异步设置内容 SETHTML

'
) }, resetHtml() { this.editor.setHtml('') }, getText() { console.log(this.editor.getText()); }, focus() { this.editor.focus() // 获取焦点,光标的位置默认会回到之前的位置闪烁 }, saveHtml() { axiosInstance({ url: "http://127.0.0.1:8083/article/save", method: 'POST', data: { htmlContent: this.htmlContent }, }).then(res => { console.log('成功'); }) }, playToolbar() { const toolbar = DomEditor.getToolbar(this.editor) const curToolbarConfig = toolbar.getConfig() /* 当前菜单排序和分组, 目的是可以知道默认的工具栏配置 */ console.log(curToolbarConfig.toolbarKeys) /* 可获取编辑器所有菜单 */ console.log(this.editor.getAllMenuKeys()); console.log(curToolbarConfig === this.toolbarConfig) // false, 不是同一个 }, enableEdit() { /* 开启或禁用 编辑 */ if (this.editorEnabled) { this.editor.disable() } else { this.editor.enable() } this.editorEnabled = !this.editorEnabled }, getUploadImageMenuConfig() { /* 获取图片上传菜单的默认配置 */ console.log(this.editor.getMenuConfig('uploadImage')); console.log(JSON.stringify(this.editor.getMenuConfig('uploadImage'))); // 获取 uploadImage菜单 的当前配置 /* { "server":"", "fieldName":"wangeditor-uploaded-image", "maxFileSize":2097152, "maxNumberOfFiles":100, "allowedFileTypes":[ "image/*" ], "meta":{ }, "metaWithUrl":false, "withCredentials":false, "timeout":10000, "base64LimitSize":0 } */ } }, mounted() { // 模拟 ajax 请求,异步渲染编辑器 setTimeout(() => { this.htmlContent = '

模拟 Ajax 异步设置内容 HTML

'
}, 1500) }, beforeDestroy() { const editor = this.editor if (editor == null) return editor.destroy() // 组件销毁时,及时销毁编辑器 } }
script>

request.js

import axios from 'axios'
import router from '@/router'

const instance = axios.create({
    baseURL: 'http://localhost:8083',
    timeout: 60000,
    withCredentials: true /* 需要设置这个选项,axios发送请求时,才会携带cookie, 否则不会携带 */
})

// Add a request interceptor
instance.interceptors.request.use(function (config) {
    // Do something before request is sent


    return config;
  }, function (error) {
    // Do something with request error
    return Promise.reject(error);
  });

// Add a response interceptor
instance.interceptors.response.use(function (response) {
    // Any status code that lie within the range of 2xx cause this function to trigger
    // Do something with response data
    console.log('收到响应',response);

    if(response.data.code == 401) {
        router.push('/login')
    }

    return response.data.data;
  }, function (error) {
    // Any status codes that falls outside the range of 2xx cause this function to trigger
    // Do something with response error
    return Promise.reject(error);
  });

export default instance

WebConfig

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry
                .addMapping("/**")
                .maxAge(3600)
                .allowCredentials(true)
                .allowedOrigins("*")
                .allowedMethods("*")
                .allowedHeaders("*")
                .exposedHeaders("token","Authorization")
        ;
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/img/**")
                .addResourceLocations("file:/D:\\Projects\\vue-springboot\\src\\main\\resources\\static\\img\\");
    }
}

ArticleController

@RestController
@RequestMapping("article")
public class ArticleController {

    @PostMapping("uploadImg")
    public Result uploadImg(@RequestParam("mfile") MultipartFile mfile) throws IOException {
        String filename = mfile.getOriginalFilename();
        mfile.transferTo(new File("D:\\Projects\\vue-springboot\\src\\main\\resources\\static\\img\\"+filename));
        return Result.ok(filename);
    }
}

yml配置

server:
  port: 8083

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://localhost:3306/vue-springboot?serverTimezone=UTC&useSSL=false
    username: root
    password: root
    driver-class-name: com.mysql.jdbc.Driver
  servlet:
    multipart:
      max-file-size: 50MB
      max-request-size: 50MB

mybatis-plus:
  mapper-locations: classpath:/mapper/**.xml

你可能感兴趣的:(前端学习,javascript,前端,vue.js)