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

news2024/9/28 9:44:13

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>

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

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

相关文章

【Spring Cloud】Spring Cloud 概述

Spring Cloud 概述 1. 认识微服务1.1 单体架构1.2 集群和分布式架构集群和分布式 1.3 微服务架构分布式架构&微服务架构 1.4 微服务带来的挑战优势挑战 2. 微服务解决⽅案- Spring Cloud2.1 什么是Spring Cloud2.2 Spring Cloud版本Spring Cloud和SpringBoot的关系 2.3 Spr…

Linux部署RabbitMQ

目录 一、环境 二、开始安装 1、安装Erlang 2、安装RabbitMQ 3、修改配置文件 先复制覆盖配置文件&#xff0c;根据自己的版本进行路径更改 打开配置文件&#xff0c;53行 去掉注释%%和逗号 4、安装插件并启动服务 web管理界面工具 重新启动 RabbitMQ 服务 查看节点…

java日志门面之JCL和SLF4J

文章目录 前言一、JCL1、JCL简介2、快速入门3、 JCL原理 二、SLF4J1、SLF4J简介2、快速入门2.1、输出动态信息2.2、异常信息的处理 3、绑定日志的实现3.1、slf4j实现slf4j-simple和logback3.2、slf4j绑定适配器实现log4j3.2、Slf4j注解 4、桥接旧的日志框架4.1、log4j日志重构为…

什么是原生IP?

代理IP的各个类型称呼有很多&#xff0c;且它们在网络使用和隐私保护方面扮演着不同的角色。今天将探讨什么是原生IP以及原生IP和住宅IP之间的区别&#xff0c;帮助大家更好地理解这两者的概念和实际应用&#xff0c;并选择适合自己的IP类型。 一、什么是原生IP&#xff1f; 原…

流程、程序和政策之间的差异

流程、程序和政策是公司遵循的指导方针&#xff0c;以确保公司以有效和安全的方式运营。 每个企业都需要它们&#xff0c;但有时可能会让人搞不清一个从哪里开始&#xff0c;另一个从哪里结束。 企业经常混淆它们的用法&#xff0c;或者在真正含义上指错一个。 从高层次来看…

“数字武当”项目荣获2024年“数据要素×”大赛湖北分赛文化旅游赛道一等奖

9月26日&#xff0c;由国家数据局、湖北省人民政府指导的首届湖北省数据要素创新大会暨2024年“数据要素”大赛湖北分赛颁奖仪式在湖北武汉举行。由大势智慧联合武当山文化旅游发展集团有限公司参报的武当山“数字武当”项目&#xff0c;荣获文化旅游赛道一等奖。 据悉&#x…

《Linux从小白到高手》理论篇(三):vi/vim编辑器和Linux文件处理“三剑客”(sed/grep/awk)

List item 本篇介绍vi/vim编辑器和Linux文件处理“三剑客”&#xff08;sed/grep/awk&#xff09;&#xff0c;这5个工具命令可能是Linux最最常用的&#xff0c;而且功能超级强大。 vi/vim vi和vim的基本介绍 所有的 Linux 系统都会内建 vi 文本编辑器。Vim 具有程序编辑的…

【frp】frp重启、frp启动、frp后台启动、frps dashboard等等

我写的关于frp配置的文章&#xff1a;frp配置 服务端frps 1. 创建服务文件 sudo nano /etc/systemd/system/frps.service2. 添加服务配置 在打开的文件中添加以下内容&#xff1a; [Unit] DescriptionFRPS Server Afternetwork.target[Service] Typesimple ExecStart/root…

LeetCode[简单] 136. 只出现一次的数字

给你一个 非空 整数数组 nums &#xff0c;除了某个元素只出现一次以外&#xff0c;其余每个元素均出现两次。找出那个只出现了一次的元素。 你必须设计并实现线性时间复杂度的算法来解决此问题&#xff0c;且该算法只使用常量额外空间。 思路 当 A 与 B 不同时&#xff0c;按…

代理模式简介:静态代理VS与动态代理

代理模式&#xff1a;静态代理VS动态代理 1、定义2、分类2.1 静态代理2.2 动态代理 3、使用场景4、总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 1、定义 代理模式是一种设计模式&#xff0c;通过代理对象控制对目标对象的访问。简而…

Stable Diffusion 使用详解(13)--- 3D纹理增强

目录 背景 Normal Map 描述 原理 使用心得 例子 描述 原图 参数设置 底模 ​编辑 正负相关性提示词 其他参数 controlnet 效果 还能做点啥 调整 效果 背景 实际上&#xff0c;在stable diffusion 中&#xff0c;你获取发现很多controlnet 其实功能有点类似&…

【优化】Vite手动分包

前言 每次打包完成后&#xff0c;默认情况下会将第三方库和自己的代码统统打包到一个JS文件中 打包后的 JS 对应一个指纹&#xff0c;当修改了自己的业务代码并重新打包后&#xff0c;还会将第三方库重新打包&#xff0c;继而生成一个新指纹&#xff0c;浏览器发现指纹变了&am…

从4道入门题目里面看Java和C的差别

目录 1.如何在IDEA进行循环输入 1.1题目概述 1.2循环输入 1.3println和print 2.如何调用数学函数 3.如何生成随机数字 4.字符串比较的方法 4.1错误案例分析 4.3正确比较方法 1.如何在IDEA进行循环输入 1.1题目概述 这个也是牛客上面的一个题目引发的思考&#xff1a;…

MySQl查询分析工具 EXPLAIN ANALYZE

文章目录 EXPLAIN ANALYZE是什么Iterator 输出内容解读EXPLAIN ANALYZE和EXPLAIN FORMATTREE的区别单个 Iterator 内容解读 案例分析案例1 文件排序案例2 简单的JOIN查询 参考资料&#xff1a;https://hackmysql.com/book-2/ EXPLAIN ANALYZE是什么 EXPLAIN ANALYZE是MySQL8.…

Linux进程的学习(持续更新)

冯诺依曼体系结构 概念&#xff1a; 冯・诺依曼体系结构是一种计算机体系结构&#xff0c;由美籍匈牙利科学家约翰・冯・诺依曼提出。它奠定了现代计算机的基本结构。 计算机分为以上五大部件组成&#xff1a; 输入设备&#xff1a;键盘&#xff0c;鼠标&#xff0c;网卡&…

【前端框架对比和选择】React 与 Vue 框架设计思路对比

框架总览 前端框架繁多&#xff0c;在学习的时候也会陷入困惑&#xff0c;我们应该抓住最主流的内容 Vue/React&#xff0c;深入底层&#xff0c;尝试揣摩框架作者的设计思路&#xff0c;开阔前端培训自己的视野&#xff0c;大家也不要把自己限制在框架之中&#xff0c;认为工…

Python | Leetcode Python题解之第442题数组中重复的数据

题目&#xff1a; 题解&#xff1a; class Solution:def findDuplicates(self, nums: List[int]) -> List[int]:ans []for x in nums:x abs(x)if nums[x - 1] > 0:nums[x - 1] -nums[x - 1]else:ans.append(x)return ans

气膜健身馆:提升运动体验与健康的理想选择—轻空间

近年来&#xff0c;气膜健身馆作为一种新兴的运动场所&#xff0c;正逐渐受到越来越多健身爱好者的青睐。这种独特的建筑形式不仅提供了良好的运动环境&#xff0c;更在健康和运动表现上展现出诸多优势。 优越的空气质量 气膜结构的核心技术通过内外气压差形成稳定的气膜&#…

Mysql调优之索引优化(四)

一、mysql索引结构B树原理 B树开始就是n树&#xff0c;不是二叉树 B树的非叶子结点存储了数据&#xff0c;导致层级会很深&#xff0c;每一层又有数据又有索引。 B树只有叶子结点存储数据&#xff0c;其余都是存储索引&#xff0c;增加了每层存取索引的数量&#xff08;3层结构…

Linux开源网络:高性能数据平面

数据平面的性能在很大程度上取决于网络 I/O 的性能&#xff0c;而网络数据包从网卡到用户空间的应用程序需要经历多个阶段&#xff0c;本文从数据平面基础到NFV&#xff0c;NFC基础设施再到OVS-DPDK VPP进行概论上的描述。 部分内容来源于《Linux开源网络全栈详解&#xff1a;从…