初始化:
-
npm install npm install tinymce -S
-
npm install @tinymce/tinymce-vue -S
-
下载完依赖包后,我们在根目录的public文件夹种新建 tinymce文件夹存放文件。在tinymce中新建langs文件夹把下载好的中文js文件放入,此处为中文语言包或者富文本皮肤
-
中文语言包下载 Language Packages | Trusted Rich Text Editor | TinyMCE
-
tinymce 插件使用可以下载至本地文件夹放入node_modules中
7. 直接粘贴第三方网站内容,让图片自动存入自己的服务器,具体代码参考注释部分;从word,text等文件中直接粘贴图片,存入本地,和后端已商议存储方式,由于领导认为无需如此操作,后续工作未完成;有解决方案的朋友可以私信我
<template>
<!-- 富文本 -->
<div>
<editor style="z-index: 5000" class="el-textarea__inner" v-model="content" :init="init"></editor>
</div>
</template>
这里需要注意 tinymce-vue 和tinymce的版本,高版本的tinymce不兼容
"@tinymce/tinymce-vue": "^5.1.1","tinymce": "^5.10.9","vue": "^3.4.21"。
<script setup>
//引入tinymce编辑器
import Editor from "@tinymce/tinymce-vue";
//引入方式引入node_modules里的tinymce相关文件文件
import tinymce from "tinymce/tinymce"; //tinymce默认hidden,不引入则不显示编辑器
import "tinymce/themes/silver"; //编辑器主题,不引入则报错
import "tinymce/icons/default"; //引入编辑器图标icon,不引入则不显示对应图标
// 引入编辑器插件
import "tinymce/plugins/advlist"; //高级列表
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/emoticons"; //表情
import "tinymce/plugins/fullscreen"; //全屏
import "tinymce/plugins/help"; //帮助
import "tinymce/plugins/image"; //插入编辑图片
import "tinymce/plugins/importcss"; //引入css
import "tinymce/plugins/insertdatetime"; //插入日期时间
import "tinymce/plugins/link"; //超链接
import "tinymce/plugins/lists"; //列表插件
import "tinymce/plugins/media"; //插入编辑媒体
import "tinymce/plugins/nonbreaking"; //插入不间断空格
import "tinymce/plugins/pagebreak"; //插入分页符
import "tinymce/plugins/preview"; //预览
import "tinymce/plugins/save"; //保存
import "tinymce/plugins/searchreplace"; //查找替换
import "tinymce/plugins/table"; //表格
import "tinymce/plugins/template"; //内容模板
import "tinymce/plugins/visualchars"; //显示不可见字符
import "tinymce/plugins/wordcount"; //字数统计
import "tinymce/plugins/paste"; //粘贴复制
import "tinymce/plugins/letterspacing"; //字间距,此插件为非集成插件需要下载至本地
import { onMounted } from "vue";
import {compress, compressAccurately,imagetoCanvas,dataURLtoFile} from 'image-conversion';
const action = ref('/api/File/UploadFile')
const imgUrl = ref(import.meta.env.VITE_APP_BASE_API)
import { copyUploadImg,fileUrlConvert } from "@/api/file";
const content = defineModel('content')
const props = defineProps({
showImageVideo:{
type: [Boolean, String],
default:true
},
})
let plug = "preview searchreplace autolink directionality visualchars fullscreen link template paste letterspacing code codesample table charmap pagebreak nonbreaking insertdatetime advlist lists wordcount autosave "
let bar = "fullscreen undo redo restoredraft | cut copy pastetext | forecolor backcolor bold italic underline strikethrough link | alignleft aligncenter alignright alignjustify outdent indent | \
styleselect formatselect fontselect fontsizeselect | bullist numlist | blockquote subscript superscript removeformat | \
table charmap emoticons pagebreak insertdatetime preview | code selectall | indent2em lineheight letterspacing formatpainter axupimgs paste "
let barImg = "image media "
const plugins = ref(props.showImageVideo? plug+barImg : plug)
const toolbar = ref(props.showImageVideo? bar+barImg : bar)
const newImgUrl = ref([])
const init = ref({
language_url: import.meta.env.VITE_BASE_URL+'tinymce/langs/zh_CN.js', //引入语言包文件
language: "zh_CN",
skin_url: import.meta.env.VITE_BASE_URL+ 'tinymce/skins/ui/oxide', //皮肤:浅色
content_css:import.meta.env.VITE_BASE_URL + "tinymce/skins/content/default/content.css",
min_height: 350,
max_height: 770,
toolbar_mode: "wrap",
plugins: plugins,
toolbar: toolbar,
content_style: "p {margin: 5px 0;}",
fontsize_formats: "12px 14px 16px 18px 24px 36px 48px 56px 72px",
font_formats:
"微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;宋体=simsun,serif;仿宋体=FangSong,serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;",
branding: false,
paste_webkit_styles:"all",
paste_data_images: false,
paste_as_text: props.showImageVideo? false : true,
promotion: false,
images_upload_handler: async function (blobInfo, succFun, failFun) {
console.log('上传数据',blobInfo,blobInfo.blob())
let fileBlob = blobInfo.blob()
const comSize = Math.ceil(fileBlob.size/1024) > 500? 500 : Math.ceil(fileBlob.size/1024)
console.log('fileName',fileBlob.name,fileBlob.size)
const img = new Image();
img.src = blobInfo.blobUri();
img.onload=function(){
console.log('img',img.width,img.height,Math.ceil(750*img.height/img.width))
// 配置绘制选项
const options = {
width: 750, // 设置目标宽度为800px
height:Math.ceil(750*img.height/img.width),
quality: 0.8,
};
// 开始绘制
imagetoCanvas(img, options)
.then((canvas) => {
canvas.toBlob((blob)=>{
//开始压缩
compressAccurately(blob,comSize).then((blob)=>{
const file = new File([blob], fileBlob.name, { type: fileBlob.type });
console.log('file',file)
let formData = new FormData();
formData.append("formFile", file);
copyUploadImg(formData).then(res=>{
let location = imgUrl.value + res.altertiveFilePath;
succFun(location);
}).catch()
}).catch((error)=>{})
})
})
.catch((error) => {
console.error('绘制失败', error)
});
}
},
// paste_preprocess: async function(plugin, args) {
// const images = args.content.match(/<img[^>]+/g);
// console.log('所有的img标签:', images);
// if (images) {
// args.content = images.forEach((item) => {
// const srcMatch = item.match(/src="([^"]+)"/);
// console.log('上传地址',srcMatch)
// if (srcMatch) {
// const src = srcMatch[1];
// let params = {
// 'fileUrlList':[src]
// }
// fileUrlConvert(params).then((res)=>{
// console.log('返回的路径',res[0].convertUrl)
// const newSrc = imgUrl.value+ res[0].originalUrl;
// item = item.replace(/src="([^"]+)"/g, `src="${newSrc}"`); // 替换图片链接
// return item;
// }).catch()
// }
// });
// }
// },
// init_instance_callback: (editor) => {
// // 粘贴远程图片本地化
// editor.on('paste', (e) => {
// let remoteImages = [];
// const doc = editor.getDoc();
// const imgTags = doc.getElementsByTagName('img');
// if(imgTags.length) {
// for (let img of imgTags) {
// var src = img.getAttribute('_src') || img.src || '';
// if (/^(https?|ftp):/img.test(src) && !test(src)) {
// remoteImages.push(src);
// }
// }
// }
// console.log('remoteImages',remoteImages)
// if (remoteImages.length) {
// // 需要上传的图片
// fileUrlConvert({'fileUrlList':remoteImages}).then((data)=>{
// console.log('返回路径',data)
// let i, o, item, res, _src, __src, list = data;
// for (i = 0; item = imgTags[i++];) {
// _src = item.getAttribute('_src') || item.src || '';
// for (o = 0; res = list[o++];) {
// if (_src == res.originalUrl && res.convertUrl) {// 抓取失败时不做替换处理
// __src = res.url;
// replaceImage(item, res); // 使用下面的新方法
// const replaceImage = (image = item, data = res) => {
// let src = editor.convertURL(data.url, 'src');
// let attr = {
// 'src': data.url,
// 'data-app': data.app,
// 'data-aid': data.aid
// };
// if(data.db) {
// attr['data-db'] = data.db;
// } else {
// attr['data-width'] = data.width;
// attr['data-height'] = data.height;
// }
// replaceUrlInUndoStack(image.src, data.url);
// editor.$(image).attr(attr).removeAttr('alt').removeAttr('data-mce-src');
// }
// break;
// }
// }
// }
// // 同步到textarea
// editor.save();
// }).catch()
// }
// })
// }
})
// // 设置白名单域名
// const localDomains = ['cfyun.cc', 'cfyun.top'];
// // 检测是否需要上传
// const test = function test(url) {
// if (url.indexOf(location.host) !== -1 || /(^\.)|(^\/)/.test(url)) {
// return !0;
// }
// // 白名单
// if (localDomains) {
// for (let domain in localDomains) {
// if (localDomains.hasOwnProperty(domain) && url.indexOf(localDomains[domain]) !== -1) {
// return !0;
// }
// }
// }
// return !1;
// }
// const each = function (xs, f) {
// for (var i = 0, len = xs.length; i < len; i++) {
// var x = xs[i];
// f(x, i);
// }
// };
// const map = function (xs, f) {
// var len = xs.length;
// var r = new Array(len);
// for (var i = 0; i < len; i++) {
// var x = xs[i];
// r[i] = f(x, i);
// }
// return r;
// };
// const replaceString = function (content, search, replace) {
// let index = 0;
// do {
// index = content.indexOf(search, index);
// if (index !== -1) {
// content = content.substring(0, index) + replace + content.substr(index + search.length);
// index += replace.length - search.length + 1;
// }
// } while (index !== -1);
// return content;
// };
// const replaceImageUrl = function (content, targetUrl, replacementUrl) {
// let replacementString = 'src="' + replacementUrl + '"' + (replacementUrl === 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7' ? ' data-placeholder="1"' : '');
// content = replaceString(content, 'src="' + targetUrl + '"', replacementString);
// return content;
// };
// const replaceUrlInUndoStack = function (targetUrl, replacementUrl) {
// each(editor.undoManager.data, function (level) {
// if (level.type === 'fragmented') {
// level.fragments = map(level.fragments, function (fragment) {
// return replaceImageUrl(fragment, targetUrl, replacementUrl);
// });
// } else {
// level.content = replaceImageUrl(level.content, targetUrl, replacementUrl);
// }
// });
// };
</script>