1. 安装 tinymce 依赖
我的是vue2项目,指定版本
"@tinymce/tinymce-vue": "^2.0.0",
"tinymce": "^5.0.3",
npm install @tinymce/tinymce-vue@2.0.0 -S
npm install tinymce@5.0.3 -S
2. 将tinymce 依赖包里的skins保存
3. 新建vue组件 及 oss.js 配置
<template>
<!-- 富文本 -->
<div>
<editor v-model="content" :init="init" :disabled="disabled"></editor>
</div>
</template>
<script>
import tinymce from "tinymce/tinymce";
import Editor from "@tinymce/tinymce-vue";
import "tinymce/icons/default/icons";
import "tinymce/themes/silver";
import "tinymce/plugins/image";
import "tinymce/plugins/media";
import "tinymce/plugins/table";
import "tinymce/plugins/lists";
import "tinymce/plugins/contextmenu";
import "tinymce/plugins/wordcount";
import "tinymce/plugins/colorpicker";
import "tinymce/plugins/textcolor";
import "tinymce/plugins/preview";
import "tinymce/plugins/code";
import "tinymce/plugins/link";
import "tinymce/plugins/advlist";
import "tinymce/plugins/codesample";
import "tinymce/plugins/hr";
import "tinymce/plugins/fullscreen";
import "tinymce/plugins/textpattern";
import "tinymce/plugins/searchreplace";
import "tinymce/plugins/autolink";
import "tinymce/plugins/directionality";
import "tinymce/plugins/visualblocks";
import "tinymce/plugins/visualchars";
import "tinymce/plugins/template";
import "tinymce/plugins/charmap";
import "tinymce/plugins/nonbreaking";
import "tinymce/plugins/insertdatetime";
import "tinymce/plugins/imagetools";
import "tinymce/plugins/autosave";
import "tinymce/plugins/autoresize";
import {client} from './js/oss.js'
export default {
components: {
Editor,
},
props: {
value: {
type: String,
default: "",
},
disabled: {
type: Boolean,
default: false,
},
plugins: {
type: [String, Array],
default:
"preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media template code codesample table charmap hr nonbreaking insertdatetime advlist lists wordcount textpattern autosave autoresize",
},
toolbar: {
type: [String, Array],
default:
"code undo redo restoredraft | cut copy paste pastetext | forecolor backcolor bold italic underline strikethrough link codesample | alignleft aligncenter alignright alignjustify outdent indent formatpainter | \
styleselect formatselect fontselect fontsizeselect | bullist numlist | blockquote subscript superscript removeformat | \
table image media charmap hr pagebreak insertdatetime | fullscreen preview",
},
},
data() {
return {
//初始化配置
init: {
selector: '#tinydemo',
language:'zh_CN',//注意大小写
// menubar: true, // 菜单栏显隐
language_url: "/static/tinymce/langs/zh_CN.js",
// language: "zh_CN",
skin_url: "/static/tinymce/skins/ui/oxide",
height: 370,
min_height: 370,
max_height: 770,
toolbar_mode: "wrap",
plugins: this.plugins,
toolbar: this.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,
// 图片上传
images_upload_handler: (blobInfo, success, failure) => {
// const img = 'data:image/jpeg;base64,' + blobInfo.base64()
// success(img)
const formData = new FormData();
formData.append("file", blobInfo.blob());
var filename = blobInfo.filename()
var index = filename.lastIndexOf('.')
var suffix = filename.substring(index + 1, filename.length)
client.multipartUpload(filename, blobInfo.blob()).then(function (result) {
if (result.res.requestUrls) {
console.log('返回结果', result)
success(result.res.requestUrls[0].split('?')[0])
}
}).catch(function (err) {
console.log(err)
})
},
},
content: this.value,
};
},
mounted() {
tinymce.init({});
},
methods: {},
watch: {
value(newValue) {
this.content = newValue;
},
content(newValue) {
this.$emit("input", newValue);
},
},
};
</script>
<style scoped lang="scss"></style>
let OSS = require('ali-oss')
export let client = new OSS({
region: 'oss-cn-hangzhou', // 填你的oss所在区域,例如oss-cn-shenzhen
accessKeyId: ' ', // 填你的oss的accessKeyId
accessKeySecret: '', // 填你的oss的accessSecret
bucket: 'gyh2jlj' // 你创建的路径名称
})
4. 更多富文本编辑器设置
var $select = {
example:{}, //创建的实例对象,id为key
/**
* 初始化下拉多选框
* @param string id 选择器id【必填】
* @param [{name:'',value:''}] data 下拉值【选填】
*/
init: function (id,data){
initSelect(id,data);
}
/**
* 赋值 或 获取值
* @param string id 选择器id【必填】
* @param string val 值【选填】为空则获取,多值英文逗号分隔 值样例:1,2,3
*/
,val:function(id,val){
valueSelect(id,val);
}
/**
* 显示下拉框
* @param string id 选择器id【必填】
*/
,open:function (id){
$select.example[id].opened();
}
/**
* 隐藏下拉框
* @param string id 选择器id【必填】
*/
,close:function (id){
$select.example[id].closed();
}
}
function initSelect(id,data){
if(checkEmpty(data)){
data = []
}
var tips = $("#"+id+"_select_tips").val();
var required = $("#"+id+"_select_div").attr("isRequired");
if(checkEmpty(required) || eval(required) !=true){
required=''
}else{
required='required'
}
var isSearch = $("#"+id+"_select_div").attr("isSearch");
if(checkEmpty(isSearch) || eval(isSearch) !=false){
isSearch=true;
}
var isShowValue = $("#"+id+"_select_div").attr("isShowValue");
if(checkEmpty(isShowValue) || eval(isShowValue) !=true){
isShowValue=false;
}
var disabled = $("#"+id+"_select_div").attr("disabled");
if(checkEmpty(disabled) || eval(disabled) !=true){
disabled=false;
}
var isRadio = $("#"+id+"_select_div").attr("isRadio");
var clickClose = false;
var model = {};
if(checkEmpty(isRadio) || eval(isRadio) !=true){
isRadio=false;
}else{
clickClose = true;
model = {
icon: 'hidden',
label: {
type: 'text',
}
}
}
if(data.length<=0){
isSearch = false;
}
/*layui.config({
base: ctxPath + '/js/layui-ext/xmSelect/',
}).extend({
xmSelect: 'xm-select'
}).use(['xmSelect'], function(){
var xmSelect = layui.xmSelect;*/
//渲染多选
$select.example[id] = xmSelect.render({
el: '#'+id+'_select_div',
name:id,
tips: tips,
clickClose: clickClose,
template({ item, sels, name, value }){
if(isShowValue){
return name + '<span style="position: absolute; right: 10px; color: #8799a3">'+value+'</span>'
}else{
return name
}
},
layVerify: required,
layReqText: tips,
model: model,
radio: isRadio,
filterable: isSearch,
disabled: disabled,
data: data
})
valueSelect(id,$("#"+id).val())
/*})*/
}
function valueSelect(id,val){
if(val!=null){
if(val==''){
//清空
$select.example[id].setValue([]);
}else{
$select.example[id].setValue(val.split(','))
}
$("#"+id).val(val);
}else{
var val = $select.example[id].getValue('value');
val = val.toString();
$("#"+id).val(val)
return val;
}
}
var $tinymce = {
val: function(id,val){
if(val!=null){
val = addBasePath(val);
tinyMCE.editors[id].setContent(val);
$("#"+id).val(val);
}else{
var cnt = tinyMCE.editors[id].getContent();
cnt = delBasePath(cnt);
$("#"+id).val(cnt);
return $("#"+id).val();
}
},
isReadonly: function (id,isReadonly){
if(isReadonly!=null && isReadonly){
tinymce.editors[id].setMode('readonly');//开启只读模式
}else{
tinymce.editors[id].setMode('design');//开启编辑模式
}
}
,init(id,path,height,isAutoFocus,isReadonly){
initTinyMce(id,path,height,isAutoFocus,isReadonly)
}
}
function addBasePath(str){
var reg = /src=([\"|'])((?!http).*?)([\"|'])/g;
return str.replace(reg, "src=$1"+fileUrl+"$2$3");
}
function delBasePath(str){
var reg = new RegExp("src=([\" | '])("+fileUrl+")(.*?)([\"|'])", "g");
return str.replace(reg, "src=$1$3$4");
}
function initTinyMce(id,path,height,isAutoFocus,isReadonly,){
var uploadUrl = ctxPath+"/file/upload";
tinymce.init({
selector: "#"+id,
language: 'zh_CN',
auto_focus : (isAutoFocus==null || typeof (isAutoFocus) != "boolean")?false:isAutoFocus, //自动获得焦点
readonly : (isReadonly==null || typeof (isReadonly) != "boolean")?false:isReadonly, //将编辑器设置成只读模式,不可编辑。
height : (height==null || typeof (height) != "number")?300:height, //将编辑器设置成只读模式,不可编辑。
branding: false,
draggable_modal:true,
fullscreen_native:true,//浏览器的全屏模式
toolbar_mode:'sliding',//工具栏模式
resize: 'both', //调整编辑器大小工具 可选值为:true(仅允许改变高度), false(完全不让你动), 'both'(宽高都能改变,注意引号)
plugins: 'paste code quickbars print preview searchreplace autolink fullscreen image imagetools axupimgs link media codesample table charmap emoticons hr advlist lists indent2em kityformula-editor formatpainter pagebreak bdmap 135editor',
quickbars_insert_toolbar: '',
quickbars_selection_toolbar: 'fontselect fontsizeselect | bold italic underline strikethrough | removeformat',
toolbar: 'fullscreen code undo redo formatpainter' +
'| fontselect fontsizeselect | forecolor backcolor | bold italic underline strikethrough subscript superscript ' +
'| alignleft aligncenter alignright alignjustify | indent2em lineheight outdent indent ' +
'| bullist numlist | image media axupimgs inserttable link bdmap kityformula-editor codesample | charmap emoticons | pagebreak hr blockquote '+
'| removeformat pastetext searchreplace preview 135editor',
menubar: false,
/*toolbar: 'undo redo | fullscreen |formatpainter | fontselect fontsizeselect | forecolor backcolor | bold italic underline strikethrough | alignleft aligncenter alignright alignjustify | indent2em lineheight outdent indent | bullist numlist | link image axupimgs table codesample bdmap',
menubar: 'view edit insert format table',
menu: {
edit: {title: '编辑', items: 'undo redo | cut copy paste pastetext selectall | searchreplace'},
view: {title: '查看', items: 'code | preview fullscreen | wordcount'},
insert: {title: '插入', items: 'image link media codesample kityformula-editor inserttable | pagebreak charmap emoticons hr'},
table: {title: '表格', items: 'inserttable tableprops deletetable | cell row column'},
},*/
font_formats: '宋体=simsun,serif;仿宋体=FangSong,serif;微软雅黑=Microsoft YaHei,Helvetica Neue,PingFang SC,sans-serif;苹果苹方=PingFang SC,Microsoft YaHei,sans-serif;黑体=SimHei,sans-serif;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;',
fontsize_formats: '12px 13px 14px 15px 16px 17px 18px 19px 20px 22px 24px 28px 32px',
lineheight_formats: '1 1.5 1.75 2 2.5 3 4 5',
setup : function(ed)
{
ed.on('init', function()
{
this.getDoc().body.style.fontSize = '14px';
this.getDoc().body.style.fontFamily = '宋体';
});
ed.on('change', function(e) {
$("#"+id).val(delBasePath(ed.getContent()));
});
/*ed.on('keyup', function(e) {
});*/
},
convert_urls: false, //自动转换URL 默认:true
content_style: "img {max-width:100%;height: auto;}", //解决图片超出屏幕
//paste 粘贴插件
paste_data_images: true, // 粘贴的同时能把内容里的图片自动上传
paste_enable_default_filters: false, //开启默认过滤器
paste_webkit_styles: 'all', //要保留的样式 "none" / "all" / string
//上传图片插件
// 使用 images_upload_handler 可自定义上传处理逻辑。使用该配置,则无需使用其他上传配置选项。
images_upload_handler: function (blobInfo, success, failure, progress) {
var xhr, formData;
var file = blobInfo.blob();//转化为易于理解的file对象
xhr = new XMLHttpRequest();
xhr.withCredentials = false;
xhr.open('POST', uploadUrl);
xhr.upload.onprogress = function(e){
progress(e.loaded / e.total * 100);
}
xhr.onload = function () {
var json;
if (xhr.status != 200) {
failure('HTTP错误: ' + xhr.status);
return;
}
json = JSON.parse(xhr.responseText);
json.location = fileUrl+json.location;
if (!json || typeof json.location != 'string') {
failure('无效的JSON: ' + xhr.responseText);
return;
}
success(json.location);
};
xhr.onerror = function () {
failure('图片上传失败,XHR传输错误。 Code: ' + xhr.status);
};
formData = new FormData();
formData.append('file', file, file.name);
formData.append('childName',"${path}") //自定义二级存放目录
xhr.send(formData);
},
//上传文件
file_picker_types: 'file image media',
file_picker_callback: function(callback, value, meta) {
//文件分类
var filetype='.pdf, .txt, .zip, .rar, .7z, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .mp3, .mp4';
//为不同插件指定文件类型及后端地址
switch(meta.filetype){
case 'image':
filetype='.jpg, .jpeg, .png, .gif';
break;
case 'media':
filetype='.mp3, .mp4';
break;
case 'file':
default:
}
//模拟出一个input用于添加本地文件
var input = document.createElement('input');
input.setAttribute('type', 'file');
input.setAttribute('accept', filetype);
input.click();
input.onchange = function() {
var file = this.files[0];
var xhr, formData;
xhr = new XMLHttpRequest();
xhr.withCredentials = false;
xhr.open('POST', uploadUrl);
xhr.onload = function() {
var json;
if (xhr.status != 200) {
failure('HTTP错误: ' + xhr.status);
return;
}
json = JSON.parse(xhr.responseText);
json.location = fileUrl+json.location;
if (!json || typeof json.location != 'string') {
failure('无效的JSON:' + xhr.responseText);
return;
}
callback(json.location);
};
formData = new FormData();
formData.append('file', file, file.name);
formData.append('childName',path) //自定义二级存放目录
xhr.send(formData);
};
},
});
}
var $file = {
val: function(id,type,val){
if(val==null){
var filePaths = [];
var isUploaderEnd=true;
$("#"+id+"_upload .layui-upload-content .layui-upload-li").each(function(i,elem) {
if($(elem).attr("data-state")=='false'){
isUploaderEnd=false;
return false;
}
var path = $(elem).find('.layui-file-path').attr("data-path")
if(path!=null && path!=""){
filePaths.push(path)
}
});
if(!isUploaderEnd){
return false;
}else{
return filePaths.join(",");
}
}else{
delAllFile(id);
var filePaths = val.split(",");
$.each(filePaths,function(index, val){
$file.addHtml(id,type,val);
})
}
},
addHtml: function (id,type,filePath){
addFileHtml(id,null,type,filePath,true);
},
init:function (type,id,path,exts,size,multiple){
initUpload(type,id,path,exts,size,multiple)
}
}
function delAllFile(id){
$('#'+id+'_upload').find(".layui-upload-content>.layui-upload-li").remove();
}
function openFile(e) {
var path = $(e).attr("data-path");
if (path == null || path == '') {
return;
}
window.open(path);
}
function delFile(id,e){
$(e).parent().remove();
fileNumberIntercept(id)
}
function addFileHtml(id,index,type,path,isShowDel){
if(index==null || index==''){
index = Math.floor(Math.random()*100000000+10000000).toString();
}
// 避免重复创建
if ($("#"+index).length > 0 ) {
return;
}
var html='';
if(type!='img'){
html+='<div id="'+index+'" class="layui-upload-li layui-file">';
html+='<i data-path="'+path+'" class="layui-file-path layui-file-icon layui-icon layui-icon-file" onClick="openFile(this)"></i>';
}else{
html+='<div id="'+index+'" class="layui-upload-li layui-img">';
html+='<img class="layui-file-path layui-file-img" src="'+path+'" data-path="'+path+'" onClick="openFile(this)">';
}
html+=' <i onClick="delFile(\''+id+'\',this)" class="layui-file-del layui-icon layui-icon-delete"></i>';
html+='</div>';
$("#"+id+"_upload .layui-upload-content").append(html);
if(isShowDel){
$("#"+index+" .layui-file-del").show();
}
fileNumberIntercept(id);
}
function setFilePath(index,type,path){
if(type!='img'){
$("#"+index+" .layui-file-icon").attr("data-path",path);
}else{
$("#"+index+" .layui-file-img").attr("src",path).attr("data-path",path);
}
}
function initProgressHtml(index){
// 避免重复创建
if ( $("#"+index+" .layui-file-error").length > 0) {
return;
}
var html='';
html+='<div class="layui-file-error">上传失败</div>';
html+='<div class="layui-file-progress layui-progress layui-progress-big" lay-showpercent="yes"';
html+=' lay-filter="'+index+'_filter">';
html+=' <div class="layui-progress-bar" lay-percent=""><span class="layui-progress-text"></span></div>';
html+='</div>';
$("#"+index).append(html);
}
function fileNumberIntercept(id){
var $btn = $('#'+id+'_upload_div_btn');
var multiple = $btn.data('multiple');
multiple = (multiple==null || typeof (multiple) != "boolean")?false:multiple;
var num =$('#'+id+'_upload').find(".layui-upload-content>.layui-upload-li").length;
if(!multiple && num>=1){
$btn.hide();
}else{
$btn.show();
}
}
function initUpload(type,id,path,exts,size,multiple){
var accept = 'file';
var acceptMime = '*.*';
if(exts!=null && exts!=''){
exts = exts.toUpperCase();
$("#"+id+"_upload .layui-upload-tips").show();
$("#"+id+"_upload .layui-upload-tips span").html(exts);
if(type=='img'){
accept = "images";
acceptMime = "."+exts.toLowerCase().replace(/,/g,', .');
}else{
acceptMime = "."+exts.toLowerCase().replace(/,/g,', .');
}
exts = exts.replace(/,/g,'|');
}else{
$("#"+id+"_upload .layui-upload-tips").hide();
exts=null;
if(type=='img'){
accept = "images";
acceptMime = "image/*";
}
}
layui.use(['upload', 'element', 'layer'], function(){
var $ = layui.jquery
,upload = layui.upload
,element = layui.element
,layer = layui.layer;
//常规使用 - 普通图片上传
var uploadInst = upload.render({
elem: '#'+id+"_upload_btn"
,url: ctxPath+'/file/upload' //改成您自己的上传接口
,accept:accept
,data: {"childName":path}
,exts : exts
,size : (size==null || typeof (size) != "number")?0:size
,multiple : (multiple==null || typeof (multiple) != "boolean")?false:multiple
,acceptMime: acceptMime
,before: function(obj){
//预读本地文件示例,不支持ie8
obj.preview(function(index, file, result){
addFileHtml(id,index,type,result)//初始化图片
initProgressHtml(index);//初始化进度条
element.progress(index+'_filter', '0%'); //进度条复位
$("#"+index).attr("data-state",'false');
});
}
,done: function(res, index, upload){
$("#"+index+" .layui-file-del").show();
$("#"+index).removeAttr("data-state");
if(res.flag == 'success'){
//上传成功
$("#"+index+" .layui-file-progress").fadeOut(3000);
setFilePath(index,type,res.url)//赋值链接
}else{
//上传失败
$("#"+index+" .layui-file-error").show();
$("#"+index+" .layui-file-progress").hide();
}
}
,error: function(index, upload){
//演示失败状态,并实现重传
$("#"+index).removeAttr("data-state");
$("#"+index+" .layui-file-del").show();
$("#"+index+" .layui-file-error").show();
$("#"+index+" .layui-file-progress").hide();
/*var demoText = $('#${id}_text');
demoText.html('<span style="color: #FF5722;">上传失败</span> <a class="layui-btn layui-btn-xs ${id}-reload">重试</a>');
demoText.find('.${id}-reload').on('click', function(){
uploadInst.upload();
});*/
}
//进度条
,progress: function(n, elem, res, index){
element.progress(index+'_filter', n + '%'); //可配合 layui 进度条元素使用
}
});
});
}
vue2 使用tinymce编辑器实现上传图片及粘贴word文本保留格式并粘贴图片自动上传
地址:https://blog.csdn.net/qq_40190624/article/details/125197252
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战