自学内容网 自学内容网

Vue项目快速整合WangEditor富文本编辑器

Vue项目快速整合WangEditor富文本编辑器

在这里插入图片描述
在这里插入图片描述

一、安装依赖

npm i wangeditor --save   //富文本编辑器
npm install highlight.js -S    //代码高亮
npm install dompurify  vue-dompurify-html  // 防xss 库

二、app.vue代码案例

已对接图片、视频接口 ,具体看如下代码案例 。

2.1、图片接口数据格式
{
  "data": [
    {
      "alt": "图片文字说明",
      "href": "跳转链接",
      "url": "http://localhost:8080/uploads/1727435190_2.jpg"
    }
  ],
  "errno": 0
}
2.2、视频接口数据格式
{
  "data": {
    "url": "http://localhost:8080/uploads/1727435236_20240920_092347.mp4"
  },
  "errno": 0
}
<template>
  <div id="app">
    <h1>Hello WangEditor</h1>
    <button @click="logEditorContent">输出编辑器内容</button>
    <br>
    <br>
    <div ref="editorContainer" style="height: 300px; border: 1px solid #ccc;"></div>
  </div>
</template>

<script>
import E from 'wangeditor';
import 'highlight.js/styles/github.css';

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

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

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

      this.editor.highlight = window.hljs;

      // 图片上传的回调处理逻辑
      this.editor.config.uploadImgHooks = {
        customInsert: (insertImgFn, result) => {
          if (result.errno === 0 && result.data && Array.isArray(result.data) && result.data.length > 0) {
            const imageData = result.data[0];
            if (imageData.url && typeof imageData.url === 'string' && imageData.url.trim() !== '') {
              insertImgFn(imageData.url, imageData.alt, imageData.href);
            }
          }
        },
      };

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

      this.editor.create();
    });
  },
  beforeDestroy() {
    if (this.editor) {
      this.editor.destroy();
    }
  },
  methods: {
    logEditorContent() {
      if (this.editor) {
        const content = this.editor.txt.html();  // 获取编辑器中的HTML内容
        console.log("编辑器内容:", content);
      }
    },
  },
};
</script>

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

.w-e-text-container {
  min-height: 300px;
}
</style>

go后端接口案例,可直接复制用

package main

import (
"fmt"
"github.com/gin-gonic/gin"
"mime/multipart"
"net/http"
"path/filepath"
"strings"
"time"
)

func main() {
router := gin.Default()

// 跨域中间件
router.Use(CORSMiddleware())

// 静态文件服务,将 "./uploads" 目录公开
router.Static("/uploads", "./uploads")

// 图片上传接口
router.POST("/upload", uploadFile)

// 视频上传接口
router.POST("/upload-video", uploadVideo)

// 启动服务器,监听8080端口
router.Run(":8080")
}

// 上传文件处理(图片)
func uploadFile(c *gin.Context) {
uploadCommon(c, "image")
}

// 上传视频处理
func uploadVideo(c *gin.Context) {
uploadCommon(c, "video")
}

// uploadCommon 是通用的文件上传处理函数,接受图片或视频
func uploadCommon(c *gin.Context, fileTypeExpected string) {
file, err := c.FormFile("file")
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"errno": 1,
"data":  gin.H{},
})
return
}

// 使用时间戳生成唯一文件名
filename := fmt.Sprintf("%d_%s", time.Now().Unix(), filepath.Base(file.Filename))
savePath := filepath.Join("./uploads", filename)

// 保存文件到指定路径
if err := c.SaveUploadedFile(file, savePath); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"errno": 1,
"data":  gin.H{},
})
return
}

// 判断文件类型
fileType := getFileType(file)

if fileType == fileTypeExpected {
// 如果文件类型匹配(图片或视频),根据不同类型返回相应的 JSON 响应
if fileType == "image" {
c.JSON(http.StatusOK, gin.H{
"errno": 0,
"data": []gin.H{
{
"url":  "http://localhost:8080/uploads/" + filename,
"alt":  "图片文字说明",
"href": "跳转链接",
},
},
})
} else if fileType == "video" {
c.JSON(http.StatusOK, gin.H{
"errno": 0,
"data": gin.H{
"url": "http://localhost:8080/uploads/" + filename,
},
})
}
} else {
// 文件类型不匹配
c.JSON(http.StatusBadRequest, gin.H{
"errno": 1,
"data":  gin.H{},
})
}
}

// getFileType 返回文件的 MIME 类型
func getFileType(file *multipart.FileHeader) string {
ext := strings.ToLower(filepath.Ext(file.Filename))
switch ext {
case ".jpg", ".jpeg", ".png", ".gif", ".bmp":
return "image"
case ".mp4", ".avi", ".mkv", ".mov":
return "video"
default:
return "other"
}
}

// CORSMiddleware 处理跨域请求的中间件
func CORSMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Writer.Header().Set("Access-Control-Allow-Origin", "*")
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")
c.Writer.Header().Set("Access-Control-Allow-Headers", "Origin, Content-Type, X-Auth-Token, Authorization")

// 处理预检请求
if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
return
}

c.Next()
}
}

三、预防xss代码案例

首先main.js文件装载
在这里插入图片描述

import DOMPurify from 'dompurify';

Vue.directive('dompurify-html', {
  bind(el, binding) {
    // 使用 DOMPurify 清理绑定的 HTML 内容
    el.innerHTML = DOMPurify.sanitize(binding.value);
  },
  update(el, binding) {
    // 每次数据更新时再次清理内容
    el.innerHTML = DOMPurify.sanitize(binding.value);
  }
});
<template>
  <div id="app">
    <h1>安全渲染 HTML 内容</h1>
    <div v-dompurify-html="htmlContent"></div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      // 动态 HTML 内容,可能来自用户输入或外部数据
      htmlContent: '<p οnclick="alert(\'XSS\')">这是一些<strong>带有恶意代码</strong>的HTML</p>'
    };
  }
};
</script>


原文地址:https://blog.csdn.net/weixin_52236586/article/details/142600564

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!