npm install tinymce@7.2.1 tinymce-vue@6.0.1 --save
为什么不直接引用node_modules的文件 Tiny does not recommend bundling tinymce and tinymce-vue with a module loader. Bundling these packages can be complex and error prone.
如果一定想用node_moudule 文件参考上面的链接说明即可。可以忽略复制文件这一步。
复制文件只保留min.js相关即可。可以根据自己的需求保留哪些文件即可。也可以存放在自己的cdn,使用远程链接。
TinyMceEditor.vue
<script setup lang="ts">
import { ref, defineProps, computed } from 'vue'
import Editor from '@tinymce/tinymce-vue'
import type { RawEditorOptions } from 'tinymce'
import { getUploadParams, fileUpload } from '@/components/KzUpload/src/api'
defineOptions({
name: 'BaseEditor'
})
interface ImageConfig {
maxSize: number
allowType: string[]
}
const props = defineProps<{
/** 配置 */
config: RawEditorOptions
/** 值 */
modelValue: string
/** 图片限制 */
imageConfig?: ImageConfig
}>()
const editorRef = ref()
// 图片上传自定义逻辑
/** 文档:https://www.tiny.cloud/docs/tinymce/latest/upload-images/ */
type UploadFn = RawEditorOptions['images_upload_handler']
const imageUploadHandler: UploadFn = async function (blobInfo, _) {
return new Promise(async (resolve, reject) => {
const { maxSize = 10 * 1024 * 1024, allowType = ['image/png', 'image/jpg', 'image/jpeg'] } =
props.imageConfig || {}
if (blobInfo.blob().size > maxSize) {
const msg = `图片太大了. 最大支持 ${maxSize / 1024 / 1024}MB`
console.error(msg, blobInfo.blob().size)
reject({ message: msg, remove: true })
return
}
if (!allowType.includes(blobInfo.blob().type)) {
const msg = `图片只支持${allowType.map((item) => item.replace('image/', '').split('、'))}格式`
reject({
message: msg,
remove: true
})
console.error(msg, blobInfo.blob().type)
return
}
// 自己上传图片的逻辑开始
let baseUrl = import.meta.env.VITE_BASE_URL
const { data } = await getUploadParams({
baseUrl,
appCode: 'App',
originalFilename: blobInfo.filename()
})
const res = await fileUpload(blobInfo.blob(), data, [], () => {})
// 自己上传图片的逻辑结束
resolve(res.data.downloadLocation)
return res.data.downloadLocation
})
}
// 双向绑定v-model
const modelContent = defineModel({ default: '' })
const defaultConfig = ref<RawEditorOptions>({
// 如果不设置license_key为gpl,console会一直提示一个模式问题
license_key: 'gpl',
// 编辑器的高度
height: 300,
// 移除tinymce右上角升级提示
promotion: false,
skin: 'QZD',
// 移除tinymce右下角品牌提示
branding: false,
// 设置语言包路径
language_url: `/libs/tinymce/langs/zh_CN.js`,
// 设置语言
language: 'zh_CN',
menubar: false,
// 配置插件列表
plugins:
'preview importcss searchreplace autolink save directionality code visualblocks visualchars fullscreen image link table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount help charmap quickbars emoticons accordion',
// 配置工具栏
toolbar: [
'undo redo | blocks fontfamily fontsize | bold italic underline strikethrough | lineheight align numlist bullist | link image | table | forecolor backcolor removeformat | charmap emoticons | code fullscreen preview'
],
setup: (editor) => {
editor.on('PreInit', function () {
editor.editorUpload.addFilter(function (image) {
const maxSize = 10 * 1024
const imageSize = image.width * image.height
return imageSize < maxSize
})
})
},
// 内容默认样式
content_style: '',
// 配置图片上传功能
images_upload_handler: imageUploadHandler
})
// 初始化配置
const initConfig = computed(() => {
return {
...defaultConfig.value,
...props.config,
setup: (editor: any) => {
props.config?.setup?.(editor)
defaultConfig.value?.setup?.(editor)
}
}
})
defineExpose({
editorRef
})
</script>
<template>
<Editor
ref="editorRef"
:init="initConfig"
:tinymceScriptSrc="/libs/tinymce/tinymce.min.js"
v-model="modelContent"
/>
</template>
<style>
.tox-tinymce {
border: none !important;
}
.tox-shadowhost.tox-fullscreen,
.tox.tox-tinymce.tox-fullscreen {
z-index: 2004 !important;
}
</style>
上方的组件已经做了本地化操作。主要配置就是lang字段。可以去下载文档,language-packages, 如果需要自定义修改下载的文件即可。
// TinyMceEditor.vue 文件中
const defaultConfig = {
...otherConfig,
font_size_formats:
'初号=56px 小初=48px 一号=34px 小一=32px 二号=29px 小二=24px 三号=21px 小三=20px 四号=18px 小四=16px 五号=14px 小五=12px 六号=10px',
content_style: `@import url('your-cdn-domain/front-cdn/fonts/style/tinymce.css?v1');body { font-family: SimHei; font-size: 14px; }`,
font_family_formats:
'黑体=SimHei;宋体=SimSun;微软雅黑=Microsoft YaHei,Helvetica,sans;Arial=Arial,Helvetica,sans-serif;Times New Roman=Times New Roman,Times;',
}
/* your-cdn-domain/front-cdn/fonts/style/tinymce.css?v1 */
@font-face {
font-family: 'SimSun';
src: url('../songti/Songti.ttc');
}
@font-face {
font-family: 'Microsoft Yahei';
src: url('../yahei/Microsoft-YaHei-Regular.ttc');
font-weight: 400;
}
@font-face {
font-family: 'Microsoft Yahei';
src: url('../yahei/Microsoft-YaHei-Bold.ttc');
font-weight: 700;
}
@font-face {
font-family: 'SimHei';
src: url('../heiti/STHeiti Light.ttc');
}
<!-- TinyMceEditor.vue 文件中 编辑器页面也要导入用于页面展示 -->
<style lang="scss">
@import url('your-cdn-domain/file-resource/dev/front-cdn/fonts/style/tinymce.css?v1');
</style>
tinymce 的多套皮肤,是需要付费的,如果觉得不好看就自己改tinymce/icons/default/icons.min.js文件
如果只有个别自己新增的icon,可以自己使用api 添加,(只支持svg文件。)
editor.ui.registry.addIcon('triangleUp', '<svg height="24" width="24"><path d="M12 0 L24 24 L0 24 Z" /></svg>');
具体原因尚未定位到,目前推测是link插件内部做了某些格式化导致的,具体表现为:
<a href="https://www.baidu.com?id=1&search=2&tag=3">https://www.baidu.com?id=1&search=2</a>
当以上内容通过insertContent
方法插入到编辑器中时,再编辑器中回车操作时,当前内容会变成
<a href="https://www.baidu.com?id=1&search=2">https://www.baidu.com?id=1&search=2</a>
编辑器会自动将href更新为https://www.baidu.com?id=1&search=2
, 也就是保持跟a标签内容一致,从而导致属性丢失。
最终解决方案是,将a标签的内容变成更href毫无关联的内容,就不会替换。
<a href="https://www.baidu.com?id=1&search=2&tag=3">百度搜索</a>
后续可以跟进此issue After insertContent 'a' tag, the href parameter is lost after pressing enter.