WangEditor快速实现版

WangEditor快速实现版

效果

案例代码

后端

Java 复制代码
package com.diy.springboot.controller;

import cn.hutool.core.util.IdUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiImplicitParam;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

@Api(tags = "富文本编辑器文件上传接口")
@RestController
@RequestMapping("/editor")
public class WangEditorUploadController {

    @Value("${files.upload.path}")
    private String fileUploadPath;

    @ApiOperation(value = "上传图片或视频", notes = "支持图片(jpg,jpeg,png,gif,bmp)和视频(mp4,avi,mkv,mov)格式")
    @ApiImplicitParam(name = "file", value = "文件", required = true, dataType = "MultipartFile")
    @PostMapping("/upload")
    public ResponseEntity<Map<String, Object>> upload(@RequestParam MultipartFile file) {
        if (file.isEmpty()) {
            return ResponseEntity.badRequest().body(createErrorResponse());
        }

        String originalFilename = file.getOriginalFilename();
        String fileType = getFileType(originalFilename);
        if ("other".equals(fileType)) {
            return ResponseEntity.badRequest().body(createErrorResponse());
        }

        try {
            String fileUUID = IdUtil.fastSimpleUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
            File uploadFile = new File(fileUploadPath + fileUUID);

            File parentFile = uploadFile.getParentFile();
            if (!parentFile.exists()) {
                parentFile.mkdirs();
            }

            file.transferTo(uploadFile);
            return ResponseEntity.ok(createImageResponse(fileUUID));
        } catch (IOException e) {
            return ResponseEntity.badRequest().body(createErrorResponse());
        }
    }

    @ApiOperation(value = "获取文件类型", hidden = true)
    private String getFileType(String filename) {
        String ext = filename.substring(filename.lastIndexOf(".")).toLowerCase();
        switch (ext) {
            case ".jpg":
            case ".jpeg":
            case ".png":
            case ".gif":
            case ".bmp":
                return "image";
            case ".mp4":
            case ".avi":
            case ".mkv":
            case ".mov":
                return "video";
            default:
                return "other";
        }
    }

    @ApiOperation(value = "创建错误响应", hidden = true)
    private Map<String, Object> createErrorResponse() {
        Map<String, Object> response = new HashMap<>();
        response.put("errno", 1);
        response.put("data", Collections.emptyMap());
        return response;
    }

    @ApiOperation(value = "创建图片响应", hidden = true)
    private Map<String, Object> createImageResponse(String filename) {
        Map<String, Object> response = new HashMap<>();
        response.put("errno", 0);

        Map<String, String> imageData = new HashMap<>();
        imageData.put("url", "http://localhost:9090/file/" + filename);
        response.put("data", imageData);

        return response;
    }
}

前端

vue 复制代码
<template>
  <div id="app">
    <h1>富文本编辑器与HTML渲染</h1>
    <el-row>
      <el-button type="primary" @click="logEditorContent">输出编辑器内容</el-button>
      <el-button type="success" @click="loadSampleContent">回显内容</el-button>
      <el-button type="warning" @click="renderUnsafeHtml">渲染HTML(XSS演示)</el-button>
    </el-row>
    <el-divider></el-divider>
    <div ref="editorContainer" style="border: 1px solid #ccc;"></div>
    <el-divider></el-divider>
    <h2>渲染的HTML内容:</h2>
    <div v-html="renderedContent"></div>
  </div>
</template>

<script>
import E from 'wangeditor'

export default {
  name: 'App',
  data() {
    return {
      editor: null,
      renderedContent: '',
    }
  },
  mounted() {
    this.$nextTick(() => {
      this.editor = new E(this.$refs.editorContainer)

      // 配置图片上传的服务器接口
      this.editor.config.uploadImgServer = 'http://localhost:9090/editor/upload';
      this.editor.config.uploadFileName = 'file';

      // 配置视频上传的服务器接口
      this.editor.config.uploadVideoServer = 'http://localhost:9090/editor/upload';
      this.editor.config.uploadVideoName = 'file';

      this.editor.highlight = window.hljs;

      // 修改图片上传的回调处理逻辑,适配后端返回的数据结构
      this.editor.config.uploadImgHooks = {
        customInsert: (insertImgFn, result) => {
          if (result.errno === 0 && result.data && result.data.url) {
            // 直接使用data.url,不再假设data是数组
            insertImgFn(result.data.url);
          } else {
            console.error('图片上传失败', result);
          }
        },
      };

      // 视频上传的回调处理逻辑
      this.editor.config.uploadVideoHooks = {
        customInsert: (insertVideoFn, result) => {
          if (result.errno === 0 && result.data && result.data.url) {
            insertVideoFn(result.data.url);
          } else {
            console.error('视频上传失败', result);
          }
        },
      };

      this.editor.create()
    })
  },
  beforeDestroy() {
    if (this.editor) {
      this.editor.destroy()
    }
  },
  methods: {
    logEditorContent() {
      if (this.editor) {
        const content = this.editor.txt.html()
        console.log("编辑器内容:", content)
        this.renderedContent = content
      }
    },
    loadSampleContent() {
      if (this.editor) {
        const sampleContent = `
          <h2>欢迎使用 WangEditor</h2>
          <p>这是一个<strong>富文本编辑器</strong>示例。</p>
          <ul>
            <li>支持多种格式</li>
            <li>易于使用</li>
            <li>功能强大</li>
          </ul>
          <p>你可以在这里添加<span style="color: blue;">各种样式</span>的文本。</p>
          <blockquote>这是一个引用示例。</blockquote>
        `
        this.editor.txt.html(sampleContent)
        this.renderedContent = sampleContent
      }
    },
    renderUnsafeHtml() {
      const unsafeHtml = '<img src="x" onerror="alert(\'XSS 攻击演示\')">'
      this.editor.txt.html(unsafeHtml)
      this.renderedContent = unsafeHtml
    }
  },
}
</script>

<style>
#app {
  width: 80%;
  margin: 20px auto;
}

.w-e-text-container {
  height: 300px !important;
}
</style>
相关推荐
hyyyyy!4 小时前
《V8 引擎狂飙,Node.js 续写 JavaScript 传奇》
node.js
自在如风。5 小时前
MyBatis-Plus 使用技巧
java·mybatis·mybatis-plus
deming_su5 小时前
第八课:性能优化与高并发处理方案
nginx·性能优化·node.js
鱼骨不是鱼翅6 小时前
Mybatis操作数据库----小白基础入门
数据库·mybatis
入门级前端开发10 小时前
npm install 报错ERESOLVE
前端·npm·node.js
钢板兽13 小时前
Java后端高频面经——Spring、SpringBoot、MyBatis
java·开发语言·spring boot·spring·面试·mybatis
OpenTiny社区13 小时前
Node.js 技术原理分析系列 4—— 使用 Chrome DevTools 分析 Node.js 性能问题
前端·开源·node.js·opentiny
huangfuyk13 小时前
使用Node.js从零搭建DeepSeek本地部署(Express框架、Ollama)
node.js·express·ollama·deepseek
华洛17 小时前
老板要求接入DeepSeek,哪家提供的服务强?
前端·javascript·node.js