背景: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},"${(info.title)}")'>查看详情 </div></div> `
);
}
});
});
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——博文作者写得很详细在项目中使用该编辑器踩的一些坑,给我启发很大
希望我的实践过程能给你带来一些帮助哦~٩(๑>◡<๑)۶ 欢迎很多很多的赞嚯~