tinymce-vue——在Vue项目中应用及实现自定义功能(插入自定义卡片、修改元素支持属性等)_tinymce-vue 自定义事件-程序员宅基地

技术标签: css  html5  vue  vue.js  html  javascript  

背景:vue-cli3 + ant-design-vue 搭建的后台管理系统,tinymce做为其中的富文本编辑器
需求:在富文本中插入自定义样式的卡片

最终效果 :在这里插入图片描述
在这里插入图片描述
接下来开始了解我是如何实现这个功能的吧~
step1:安装与引用(参考①)

npm install @tinymce/tinymce-vue -S
npm install tinymce -S

在editor.vue中使用

<template>
  <div class="editor-wrapper">
    <Editor
      api-key="............" // 经过实践直接引入插件失败报错后,找了一个key插入,然后就可正常使用了
      v-model="content"
      @onChange="onChange"
      ref="editor"
      :init="init"
    ></Editor>
  </div>
</template>
<script>
import Editor from "@tinymce/tinymce-vue";
export default {
    
  props: {
    
    h5Content: {
    
      type: String,
      initialValue: ``
    }
  },
  components: {
    
    Editor
  },
  data() {
    
    return {
    
      content: "",
      init: {
    },
      editor: null
    };
  },
  methods: {
    
    onChange(e) {
    
      this.$emit('changeContent', this.content)
    }
  },
  created() {
    
    const that = this;
    const uploadUrl = this.$api.upload
    this.init = {
    
      content_css: "https://xxxxx.css", // 内容样式
      language_url: "https://xxxxx.com/zh_CN.js", // 中文包地址
      language: "zh_CN",
      height: 800, // 高度
      branding: false,
      menu: {
    
        favs: {
    
          title: "My Favorites",
          items: "code visualaid | searchreplace | spellchecker | emoticons"
        }
      },
      menubar: "favs file edit view insert format tools table help",
      plugins: [
        "advlist autolink lists link image charmap print preview anchor",
        "searchreplace visualblocks code fullscreen",
        "insertdatetime media table paste code help wordcount"
      ],
      toolbar:
        "CardBtn | undo redo | formatselect | bold italic backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | image removeformat",
      setup: function(editor) {
    
        // 注册一个icon
        editor.ui.registry.addIcon(
          "shopping-cart",
          `<svg viewBox="0 0 1024 1024" data-icon="shopping-cart" width="1.5em" height="1.5em" fill="currentColor" aria-hidden="true" focusable="false" class=""><path d="M922.9 701.9H327.4l29.9-60.9 496.8-.9c16.8 0 31.2-12 34.2-28.6l68.8-385.1c1.8-10.1-.9-20.5-7.5-28.4a34.99 34.99 0 0 0-26.6-12.5l-632-2.1-5.4-25.4c-3.4-16.2-18-28-34.6-28H96.5a35.3 35.3 0 1 0 0 70.6h125.9L246 312.8l58.1 281.3-74.8 122.1a34.96 34.96 0 0 0-3 36.8c6 11.9 18.1 19.4 31.5 19.4h62.8a102.43 102.43 0 0 0-20.6 61.7c0 56.6 46 102.6 102.6 102.6s102.6-46 102.6-102.6c0-22.3-7.4-44-20.6-61.7h161.1a102.43 102.43 0 0 0-20.6 61.7c0 56.6 46 102.6 102.6 102.6s102.6-46 102.6-102.6c0-22.3-7.4-44-20.6-61.7H923c19.4 0 35.3-15.8 35.3-35.3a35.42 35.42 0 0 0-35.4-35.2zM305.7 253l575.8 1.9-56.4 315.8-452.3.8L305.7 253zm96.9 612.7c-17.4 0-31.6-14.2-31.6-31.6 0-17.4 14.2-31.6 31.6-31.6s31.6 14.2 31.6 31.6a31.6 31.6 0 0 1-31.6 31.6zm325.1 0c-17.4 0-31.6-14.2-31.6-31.6 0-17.4 14.2-31.6 31.6-31.6s31.6 14.2 31.6 31.6a31.6 31.6 0 0 1-31.6 31.6z"></path></svg>`
        );
        // 注册一个自定义的按钮
        editor.ui.registry.addButton("CardBtn", {
    
          icon: `shopping-cart`,
          onAction: function(_) {
    
            that.isShowCard = true;
            that.editor = editor;
          }
        });
      },
      images_upload_handler: function(blobInfo, success, failure) {
    
        // 自定义图片上传
        let xhr, formData;
        xhr = new XMLHttpRequest();
        xhr.withCredentials = false;
        xhr.open(
          "POST",
          uploadUrl
        );
        xhr.onload = function() {
    
          let json;
          if (xhr.status != 200) {
    
            failure("HTTP Error: " + xhr.status);
            return;
          }
          json = JSON.parse(xhr.responseText);
          if (!json || typeof json.data[0] != "string") {
    
            failure("Invalid JSON: " + xhr.responseText);
            return;
          }
          success(json.data[0]);
        };
        formData = new FormData();
        formData.append("file", blobInfo.blob(), blobInfo.filename());
        xhr.send(formData);
      }
    };
  }
};
</script>

step2:撰写自定义的标签
创建一个addCard.vue(我使用弹窗的方式,采用了antd的UI组件)
此处可根据业务需求撰写

step3:在editor.vue中引入addCard组件

<template>
  <div>
    ...
    <AddCard @cancel="isShowCard = false" @create="createCard" ref="addCard" :visible="isShowCard" />
  </div>
</template>

<script>
import AddCard from "./add-card"; // 引入组件
export default {
    
  components: {
    
    Editor,
    AddCard
  },
  data() {
    
    return {
    
      content: "",
      init: {
    }, // editor初始化
      isShowCard: false, // 是否显示插件
      editor: null
    };
  },
   methods: {
    
    createCard() {
    
      const form = this.$refs.addCard.form;
      const editor = this.editor;
      const _this = this;
      this.isShowCard = false;
      form.validateFields((err, values) => {
    
        if (err) return;
        let goods = [...values.goods];
        goods.forEach(e => {
    
          _this.$api.content
            .goodsSkuList({
    
              page: 1,
              limit: 1,
              id: e.key
            })
            .then(res => {
    
              if (res.data) {
    
                let data = res.data[0];
                let info = {
    
                  id: data.id,
                  title: data.name,
                  price: data.price,
                  mainImage: data.mainImageUrl
                };
                info.price = _this.toDecimal2(info.price);
                // 采用editor.insertContent方法插入卡片结构
                editor.insertContent(
                  `<div class='goodsBox'><img class='goodsImg' src='${
      info.mainImage}'/><div class='goodsInfo'><span class='goodsTitle'>${
      info.title}</span><span class='goodsPrice'><span class='priceSymbol'>¥</span>${
      info.price}</span></div><div class='goodsBtn' οnclick='goodClick(${
      info.id},&quot;${
      (info.title)}&quot;)'>查看详情 </div></div>&nbsp;`
                );
              }
            });
        });
        this.$emit("checkGoods", goods);
        form.resetFields();
      });
    }
  },
};
</script>

至此,咱们的自定义卡片功能就完成…90%,哈哈哈~为啥还有10%咧,这就是我踩得坑了o(╥﹏╥)o
在完成上面步骤后,我尝试添加了卡片,结果发现出来的卡片中“查看详情”的div缺少了onclick…我的onclick被“吞掉”了!
踩坑①:通过查询资料后发现标签中支持的属性还需要去配置

// 该配置添加在Init中
extended_valid_elements:'a[class|target|href|onclick],div[class|onclick|id|style],link[rel|href]',

同时,在实践中发现另一个坑
踩坑②:加粗效果是给内容包裹了<strong> 标签(但在某些页面上无法正常展示这个效果)
查询资料后发现可以通过以下方式修改加粗功能为使用<span>标签包裹

// 同样在init中配置
formats:{
    
        bold: {
     inline: 'span', styles: {
    'font-weight': 'bold'} }
      },

参考资料:
①TinyMCE中文手册——基本使用上的问题都能够解决
②TinyMCE英文文档(用于搜索API)——API很全面,还有例子,很不错
③在Vue项目中引入tinymce——博文作者写得很详细在项目中使用该编辑器踩的一些坑,给我启发很大

希望我的实践过程能给你带来一些帮助哦~٩(๑>◡<๑)۶ 欢迎很多很多的赞嚯~

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/a19802007852/article/details/106207284

智能推荐

docker删除重装,以及极其重要的/etc/docker/key.json文件-程序员宅基地

文章浏览阅读8.9k次,点赞2次,收藏2次。先说以下我为何要删除docker的原因吧:因为我感觉Docker Hub有点慢,就配置了阿里云的镜像加速器,可是按阿里云的官方配置完后我怎么感觉它更慢了,对比昨天配置阿里的maven镜像仓库后快到起飞的速度,我认为是此次配置没有生效。多次确认新加入的/etc/docker/demon.json文件无误后又多次systemctl了未果,随即我怀疑阿里给出的以下方案中的“修改”的/etc/dock...

空间物理——概述_空间物理概论-程序员宅基地

文章浏览阅读1.9k次,点赞3次,收藏4次。文章目录空间物理的研究对象太阳风能量向地球传输的三种方式和所需要的时间太阳内部结构、太阳活动太阳内部结构太阳活动太阳风速度从太阳表面到地球轨道附近变化参考空间物理的研究对象大气层:10KM以上,分成平流层、中层、低热层、热层、逃逸层电离层:60-90KM以上,一直到1000KM左右,部分电离气体,中性成风碰撞的影响不可忽略地球磁层:完全电离的气体,1000KM以上,可忽略碰撞,有太阳风和..._空间物理概论

BQ4050学习笔记(二)-程序员宅基地

文章浏览阅读2.9k次,点赞5次,收藏25次。BQ4050学习笔记(二)永久失效:如果发⽣严重故障,该设备可以永久禁⽤电池组。永久故障检查(IFC 和 DFW 除外)可以通过设置Settings:Enabled PFA、 Settings:Enabled PF B、 Settings:Enabled PF C 和Settings:Enabled PF D 中的相应位单独启⽤或禁⽤。所有永久在设置ManufacturingStatus()[PF]之前,故障检查(IFC 和 DFW 除外)被禁⽤。当任何PFStatus()位置位时,器件进⼊ PER_bq4050

SpringCloudAlibaba-Nacos注册中心的使用_spring-cloud-alibaba 使用nacos 2.1版本-程序员宅基地

文章浏览阅读152次。第二步:填写配置文件参数,这里定义了一个名字为application-user-dev.yaml的配置,使用的是YAML格式。DataID : 非常重要,可以看做是配置的文件的名字,在程序中拉取配置文件的时候需要指定Data ID。如果不使用默认的public命名空间,那么需要指定namespace配置为要使用的命名空间的Id值。第一步:打开Nacos监控面板,进入配置列表,新增一个user服务的配置文件。进入配置列表 ,切换到新建立的命名空间,创建配置文件。修改Nacos,添加命名空间。_spring-cloud-alibaba 使用nacos 2.1版本

CVE-2023-21716 Microsoft Word远程代码执行漏洞Poc_cve-2023-21716复现-程序员宅基地

文章浏览阅读293次。受害者打开python代码生成的RTF文件,RTF解析器解析恶意代码,触发堆溢出,Microsoft Word会闪退,用户其它Word中未保存的内容会丢失。_cve-2023-21716复现

c语言程序设计让a变成A,c语言程序设计a期末模拟试题.docx-程序员宅基地

文章浏览阅读451次。文件排版存档编号:[UYTR-OUPT28-KBNTL98-UYNN208]文件排版存档编号:[UYTR-OUPT28-KBNTL98-UYNN208]C语言程序设计A期末模拟试题C语言程序设计A期末模拟试题一一、单项选择题(每小题2分,共20分)由C++目标文件连接而成的可执行文件的缺省扩展名为( )。A. cpp B. exe C. obj D. li..._c语言如何将a转换成a

随便推点

利用beef和msf实现session远程命令_msf如何切换一个session-程序员宅基地

文章浏览阅读534次。笔记beef启动 beef 的方法msfbeef工具目录 /usr/share/beef-xss配置文件 config.yaml启动 beef 的方法1.beef-xss2./usr/share/beef-xss/beef(使用前需要修改默认的用户名称和密码)Web 管理界面 http://127.0.0.1:3000/ui/panelShellcode http://127.0.0.1:3000/hook.js测试页面 http://127.0.0.1:3000/demos/butch_msf如何切换一个session

python判断丑数_丑数问题及变种小结-程序员宅基地

文章浏览阅读503次。丑数问题及变种小结声明1 判断丑数因子只包含2,3,5的数称为丑数(Ugly Number),习惯上把1当作第一个丑数面试lintcode 517 ugly numbersegmentfault剑指offer 面试题34 丑数数组解法:参考剑指offer,将待判断目标依次连续整除2,3,5,若是最后获得1,证实该数为丑数;优化/*** 依次整除2,3,5判断(2,3,5顺序判断时间最优)* htt..._编写python来证明一个数是丑数

python自动化测试实战 —— WebDriver API的使用_python webdriver api-程序员宅基地

文章浏览阅读1.9k次,点赞30次,收藏11次。Selenium 简介: WebDriver是Selenium Tool套件中最重要的组件。Selenium 2.0之后已经将Selenium和WebDriver进行合并,作为一个更简单、简洁、有利于维护的API提供给测试人员使用。 它提供了一套标准的接口,可以用多种编程语言调用,并且和浏览器进行交互。 WebDriver可以对浏览器进行控制,包括输入URL,点击按钮,填写表单,滚动页面,甚至是执行JavaScript代码。同时,它也能够获取网页中的信息,如文本,标签,属_python webdriver api

Nodejs crypto模块公钥加密私钥解密探索_crypto.publicencrypt-程序员宅基地

文章浏览阅读1w次。1.什么是公钥加密私钥解密 简单一点来说一般加密解密都用的是同一个秘钥或者根本不用,而这里采用的是加密用一个秘钥,解密用另一个秘钥且能解密成功.这就属于不对称加密解密算法的一种了.2.公钥秘钥的生成 由于这种加密方案,公钥秘钥是成对的,所以需要一些工具生成 利用 openssl 生成公钥私钥 生成公钥: openssl genrsa -out rsa_private_key...._crypto.publicencrypt

Maven简明教程(5)---依赖关系(实例篇)_依赖关系怎么写-程序员宅基地

文章浏览阅读1.7k次。[工欲善其事,必先利其器]上文中,我们简单介绍了依赖关系的基本理论与配置方式。但是由于这个知识点在我们日后的开发过程中会经常使用到,因此,我们在本篇中通过演示实例来说明依赖关系,请各位看官一定跟着步骤,亲自尝试一番。仔细观察通过这种方式对我们程序架构造成的影响。特别的,这里以一份已经调试完成的工程为例,因此,与前文说的工程命名不一致,敬请谅解。准备工作:a.操作系统:win7 x6_依赖关系怎么写

2017多校联合第五场1006/hdu6090Rikka with Graph(思维公式)-程序员宅基地

文章浏览阅读343次。Rikka with GraphTime Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 592 Accepted Submission(s): 353Problem DescriptionAs we know,