Files
cdTestPlant3/cdTMP/src/components/ma-editor/index.vue

180 lines
6.1 KiB
Vue
Raw Normal View History

2023-06-08 21:09:28 +08:00
<template>
<div>
<editor :key="editorKey" v-model="content" :init="initConfig" :id="props.id"></editor>
</div>
</template>
<script setup>
import { reactive, ref, watch, computed } from "vue"
import { useAppStore } from "@/store"
import Editor from "@tinymce/tinymce-vue"
import tinymce from "tinymce/tinymce"
import "tinymce/icons/default"
import "tinymce/models/dom"
import "tinymce/themes/silver"
2024-05-30 11:29:44 +08:00
// import "tinymce/plugins/advlist" // 高级列表
// import "tinymce/plugins/anchor" // 锚点
// import "tinymce/plugins/autolink" // 自动链接
import "tinymce/plugins/autosave" // 自动存稿
// import "tinymce/plugins/charmap" // 特殊字符
import "tinymce/plugins/code" // 编辑源码
// import "tinymce/plugins/codesample" // 代码示例
// import "tinymce/plugins/directionality" // 文字方向
import "tinymce/plugins/image" // 插入编辑图片
import "tinymce/plugins/importcss" // 引入css
// import "tinymce/plugins/insertdatetime" // 插入日期时间
import "tinymce/plugins/lists" // 列表插件
import "tinymce/plugins/nonbreaking" // 插入不间断空格
// import "tinymce/plugins/pagebreak" // 插入分页符
// import "tinymce/plugins/preview" // 预览
import "tinymce/plugins/quickbars" // 快速工具栏
import "tinymce/plugins/save" // 保存
import "tinymce/plugins/searchreplace" // 查找替换
import "tinymce/plugins/table" // 表格
// import "tinymce/plugins/visualblocks" //显示元素范围
import "tinymce/plugins/visualchars" // 显示不可见字符
// import "tinymce/plugins/wordcount" // 字数统计
2023-06-08 21:09:28 +08:00
const appStore = useAppStore()
const props = defineProps({
modelValue: { type: String },
2024-05-15 17:04:11 +08:00
height: { type: Number, default: 200 },
2023-06-08 21:09:28 +08:00
id: { type: String, default: () => "tinymce" + new Date().getTime().toString() },
plugins: {
type: [String, Array],
2024-05-30 11:29:44 +08:00
default: "searchreplace visualchars code table nonbreaking lists autosave"
2023-06-08 21:09:28 +08:00
},
toolbar: {
type: [String, Array],
2024-05-15 17:04:11 +08:00
// 如果要取消粘贴只粘贴文本需要用户加格式请加上pastetext
default:
2024-05-30 11:29:44 +08:00
"code undo redo restoredraft | paste | bold | alignleft alignjustify indent formatpainter | \
styleselect formatselect fontselect fontsizeselect | bullist numlist | subscript superscript removeformat"
2023-06-08 21:09:28 +08:00
}
})
const emit = defineEmits(["update:modelValue", "change"])
let content = computed({
get() {
return props.modelValue
},
set(value) {
emit("update:modelValue", value)
}
})
const list = ref([])
2024-05-30 11:29:44 +08:00
// 辅助函数遍历元素和子元素的style样式
function cleanStyles(element) {
element.removeAttribute("style") // 移除元素自身的style属性
element.removeAttribute("class") // 移除元素自身的class属性
// 遍历子元素,递归清理样式
for (const child of element.children) {
// 移除子元素的style以及class属性
cleanStyles(child)
}
}
// 辅助函数将元素中span变为text节点
function removeUnwantedSpansAndMore(element) {
// 所有span变为字符串
const spans = element.querySelectorAll("span")
for (const span of spans) {
const text = span.textContent
const textNode = document.createTextNode(text)
const parent = span.parentNode
parent.removeChild(span)
parent.appendChild(textNode)
}
// 所有a元素变成为字符串
const as = element.querySelectorAll("a")
for (const a of as) {
const text = a.textContent
const textNode = document.createTextNode(text)
const parent = a.parentNode
parent.removeChild(a)
parent.appendChild(textNode)
}
}
// 辅助函数:去掉注释节点
function removeCommentNodes(node) {
const childNodes = node.childNodes
// 遍历子节点
for (let i = childNodes.length - 1; i >= 0; i--) {
// 如果是注释节点
if (childNodes[i].nodeType === Node.COMMENT_NODE) {
node.removeChild(childNodes[i]) // 从父节点中移除该注释节点
} else if (childNodes[i].nodeType === Node.ELEMENT_NODE) {
removeCommentNodes(childNodes[i]) // 如果是元素节点,递归检查该元素的子节点
}
}
}
2023-06-08 21:09:28 +08:00
const initConfig = reactive({
menubar: false, // 菜单栏显隐
language_url: "/tinymce/i18n/zh_CN.js",
language: "zh_CN",
2023-06-15 20:13:46 +08:00
skin_url: "/tinymce/skins/ui/tinymce-5",
2023-06-08 21:09:28 +08:00
height: props.height,
toolbar_mode: "wrap",
plugins: props.plugins,
toolbar: props.toolbar,
2023-08-25 13:28:24 +08:00
skeletonScreen: true,
2023-06-08 21:09:28 +08:00
branding: false,
content_css: "/tinymce/skins/content/default/content.css",
2024-05-30 11:29:44 +08:00
selector: "#textarea1", // 下面自定义样式选中的区域为编辑区
content_style: "body {line-height:1.5;font-size:14px;} p {margin:2px 0px;}", // 这里可以设置自定义样式
// paste_as_text: false, // 粘贴文字只能是纯文本
// 1.自定义粘贴过滤器函数args.content就是粘贴内容
paste_preprocess: function (plugin, args) {
let content = args.content
const parser = new DOMParser()
const doc = parser.parseFromString(content, "text/html")
// 遍历一级元素
Array.from(doc.body.children).forEach((element) => {
cleanStyles(element)
removeUnwantedSpansAndMore(element)
removeCommentNodes(element)
console.log(element)
2023-06-08 21:09:28 +08:00
})
2024-05-30 11:29:44 +08:00
// 将处理后的fragment转换回HTML字符串
args.content = doc.body.innerHTML
2023-06-08 21:09:28 +08:00
}
})
const editorKey = ref(new Date().getTime())
2024-05-30 11:29:44 +08:00
// 图片作为img的base64处理
2023-06-08 21:09:28 +08:00
watch(
() => list.value,
(imgs) => {
let tmp = ""
imgs.map((img) => {
if (
img.indexOf(".jpg") > -1 ||
img.indexOf(".png") > -1 ||
img.indexOf(".bmp") > -1 ||
img.indexOf(".jpeg") > -1 ||
img.indexOf(".svg") > -1 ||
img.indexOf(".gif") > -1
) {
tmp += `<img src=${img} width="100%" />`
}
})
content.value = content.value ? content.value + tmp : tmp
}
)
watch(
() => content.value,
(vl) => emit("change", vl)
)
</script>
2024-05-15 17:04:11 +08:00
<style lang="less"></style>