1.下载插件
- vue2.0请下载以下版本
"tinymce" : "5.1.0"
"@tinymce/tinymce-vue" : "3.0.1"
- vue3.0请下载以下版本
"tinymce" : "5.10.3"
"@tinymce/tinymce-vue" : "5.0.0"
- 可结合下载
npm install tinymce@5.10.3 @tinymce/tinymce-vue@5.0.0 -S
2.在node_modules依赖,会出现tinymce,找到之后,复制以下四个文件,放入public目录下,用tinymce包着是个4个文件
icons - plugins - skins - themes
3.themes文件中创建一个langs目录用于放入zh-Hans.js文件
这是一个汉化版,,在最一开始时富文本是英文需要转为中文
下载解压后是一个langs文件夹里面有zh_CN.js文件,将langs文件夹放在我们刚才新建的static/tinymce文件目录下与skins文件夹同级
4. 封装的富文本
<!--
富文本编辑器
vue2.0请下载以下版本
"tinymce" : "5.1.0"
"@tinymce/tinymce-vue" : "3.0.1"
拷贝tinymce三个目录plugins,skins,themes到publiu/tinymce
---------------------------------------------------------------------
vue3.0请下载以下版本
"tinymce" : "5.10.3"
"@tinymce/tinymce-vue" : "5.0.0"
拷贝tinymce四个目录icons,plugins,skins,themes到publiu/tinymce
---------------------------------------------------------------------
汉化
publiu/tinymce/zh_CN.js 汉化,下载地址 https://www.tiny.cloud/get-tiny/language-packages/
-->
<template>
<!-- tinymceId--编译器id init--初始化数据 -->
<editor v-model="myValue" :init="init" :disabled="disabled" :id="tinymceId" ref="editorRef"></editor>
</template>
<script lang="ts" setup>
import { ref, reactive, watch, onMounted } from "vue";
// //JS部分
// //1、在js中引入所需的主题和组件
import tinymce from "tinymce/tinymce"; //富文本
// import "tinymce/skins/content/default/content.css";
import Editor from "@tinymce/tinymce-vue"; //富文本标签
import "tinymce/themes/silver/theme";
import "tinymce/icons/default"; //引入编辑器图标icon,不引入则不显示对应图标
// 2、引入相关插件
import "tinymce/icons/default/icons"; //自定义icon 操作图标
import "tinymce/plugins/image"; // 插入上传图片插件
import "tinymce/plugins/media"; // 插入视频插件
import "tinymce/plugins/table"; // 插入表格插件
import "tinymce/plugins/lists"; // 列表插件
import "tinymce/plugins/wordcount"; // 字数统计插件
import "tinymce/plugins/code"; // 源码
import "tinymce/plugins/fullscreen"; //全屏
import "tinymce/plugins/preview";
import "tinymce/icons/default";
import "tinymce/themes/silver";
import "tinymce/plugins/advlist"; //高级列表
import "tinymce/plugins/anchor"; //锚点
import "tinymce/plugins/autolink"; //自动链接
import "tinymce/plugins/autosave"; //编辑器高度自适应,注:plugins里引入此插件时,Init里设置的height将失效
import "tinymce/plugins/code"; //编辑源码
import "tinymce/plugins/codesample"; //代码示例
import "tinymce/plugins/directionality"; //文字方向
import "tinymce/plugins/fullscreen"; 全屏插件
import "tinymce/plugins/insertdatetime"; //插入日期时间
import "tinymce/plugins/link"; //链接插件
import "tinymce/plugins/lists"; 列表插件
import "tinymce/plugins/media"; //媒体插件
import "tinymce/plugins/nonbreaking"; //插入不间断空格
import "tinymce/plugins/noneditable";
import "tinymce/plugins/pagebreak"; //插入分页符
import "tinymce/plugins/paste"; //预览
import "tinymce/plugins/print"; //打印
import "tinymce/plugins/save"; //保存
import "tinymce/plugins/searchreplace"; //查找替换
// import 'tinymce/plugins/spellchecker' //拼写检查,未加入汉化,不建议使用
import "tinymce/plugins/tabfocus"; //切入切出,按tab键切出编辑器,切入页面其他输入框中
import "tinymce/plugins/template"; //内容模板
import "tinymce/plugins/textpattern"; //快速排版
import "tinymce/plugins/visualblocks"; //显示元素范围
import "tinymce/plugins/visualchars"; //显示不可见字符
import "tinymce/plugins/wordcount"; //字数统计
import "tinymce/plugins/table"; //表格插件
import "tinymce/plugins/textcolor"; //文字颜色
import "tinymce/plugins/toc"; //目录生成器
import { Upload } from "@/utils/http";
const emits = defineEmits(["getContent"]);
// 父组件传值
const props = defineProps({
value: {
type: String,
default: () => {
return "";
},
},
// 禁用富文本编辑器,由父组件传值
disabled: {
type: Boolean,
default: false,
},
// 需要加载的插件
plugins: {
type: [String, Array],
default:
"link lists image code table wordcount preview fullscreen media searchreplace insertdatetime preview print",
}, //必填
// 工具栏
toolbar: {
type: [String, Array],
default:
"fullscreen undo redo cut copy paste pastetext | table media image insertdatetime bold italic underline preview | forecolor backcolor print | alignleft aligncenter alignright alignjustify | bdmap indent2em lineheight formatpainter axupimgs letterspacing| searchreplace | bullist numlist | fontselect fontsizeselect",
// default:
// " cut copy paste pastetext undo | forecolor backcolor bold italic underline strikethrough link anchor | alignleft aligncenter alignright alignjustify outdent indent | \
// styleselect formatselect fontselect fontsizeselect | bullist numlist | blockquote subscript superscript removeformat | \
// table image media charmap emoticons hr pagebreak insertdatetime print preview | fullscreen | bdmap indent2em lineheight formatpainter axupimgs",
}, //必填
});
//用于接收外部传递进来的富文本
const editorRef = ref(null);
const myValue = ref(props.value);
const tinymceId = ref(
//富文本编辑器id
"vue-tinymce-ID-" + +new Date() + ((Math.random() * 1000).toFixed(0) + "")
);
//定义一个对象 init初始化
const init = reactive({
selector: "#" + tinymceId.value, //富文本编辑器的id,
resize: false, //右下角哪个拖动改变大小是否可用
// icons_url: '/项目名/icons/custom/icons.js', //自定义图标
language_url: "/public/tinymce/langs/zh-Hans.js", //语言包路径
language: "zh-Hans", //语言
skin_url: "/public/tinymce/skins/ui/oxide", //皮肤,自带浅色和深色,在piblic的tinymce的skins的ui里面
height: 500, //编辑器高度
branding: false, //是否禁用“Powered by TinyMCE”
menubar: true, //顶部菜单栏显示
image_dimensions: true, //去除宽高属性
plugins: props.plugins, //这里的数据是在props里面就定义好了的
toolbar: props.toolbar, //这里的数据是在props里面就定义好了的
toolbar_mode: "sliding", //工具栏模式
//字体选择
font_formats:
"Arial=arial,helvetica,sans-serif; 宋体=SimSun; 微软雅黑=Microsoft Yahei; Impact=impact,chicago;", //字体
fontsize_formats: "11px 12px 14px 16px 18px 24px 30px 36px 48px 64px 72px", //文字大小
// paste_convert_word_fake_lists: false, // 插入word文档需要该属性
paste_webkit_styles: "all", //此选项允许您指定在WebKit中粘贴时要保留的样式,true保留,false默认样式
paste_merge_formats: true, //此选项启用粘贴插件的合并格式功能
nonbreaking_force_tab: true, // 此选项允许您在用户按下键盘tab键时强制TinyMCE插入三个实体
paste_auto_cleanup_on_paste: false, //粘贴自动清理粘贴
file_picker_types: "media", // // 此选项允许您通过空格或逗号分隔的类型名称列表指定所需的文件选取器类型。目前有三种有效类型:文件、图像和媒体
insertdatetime_element: true,
insertdatetime_formats: ["%Y-%m-%d", "%H:%M:%S"],
//开始前执行
setup: function (editor: { ui: { registry: { addIcon: (arg0: string, arg1: string) => void; }; }; } ) {
editor.ui.registry.addIcon(
"image",
`<svg t="1664002320321" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4420" width="24" height="24"><path d="M125.9 185h772.2v653.9H125.9z" fill="#1F53CC" p-id="4421"></path><path d="M164.7 217.2h694.6v516.7H164.7z" fill="#FECD44" p-id="4422"></path><path d="M458.9 734l-8.6-43.8-101.5-102.8-135 146.6z" fill="#FC355D" p-id="4423"></path><path d="M306.9 348.7m-66.7 0a66.7 66.7 0 1 0 133.4 0 66.7 66.7 0 1 0-133.4 0Z" fill="#FFFFFF" p-id="4424"></path><path d="M384.6 734h474.7V608.8L687.8 400.1z" fill="#FC355D" p-id="4425"></path><path d="M422.5 662l-37.9 72 52.1-57.5z" fill="#BF2847" p-id="4426"></path><path d="M302.5 778.9h418.9v16.7H302.5z" fill="#00F0D4" p-id="4427"></path></svg>`
);
},
// content_css: "/web-official-website-manage/skins/content/default/content.css", //以css文件方式自定义可编辑区域的css样式,css文件需自己创建并引入
content_css: "/public/tinymce/skins/content/default/content.min.css", //以css文件方式自定义可编辑区域的css样式,css文件需自己创建并引入
/**
* 上传图片
* @param blobInfo 图片信息
* @param success 成功回调
* @param failure 失败回调
*/
images_upload_handler: (
blobInfo: { blob: () => string | Blob },
success: (arg0: string) => void,
failure: (arg0:string) => void
) => {
const formData = new FormData(); //创建表单对象
formData.append("file", blobInfo.blob());
Upload(formData).then((res:any) => {
success(res.url);
}).catch((err) => {
failure(err);
});
},
// 文件上传 需要的话添加
// 上传视频
file_picker_callback: (callback: (arg0: string) => void , value:Array<any> , meta: { filetype: string; }) => {
if (meta.filetype == "media") {
// callback('movie.mp4', { source2: 'alt.ogg', poster: 'image.jpg' });
let input = document.createElement("input");
input.setAttribute("type", "file");
input.setAttribute("accept", ".mp3, .mp4");
input.click();
input.onchange = function (v:any) {
console.log(v.target.files[0]); //视频文件信息 上传即可 规定视频格式大小需重写逻辑 获取视频时长
let fd = new FormData();
fd.append("file", v.target.files[0]);
Upload(fd).then((res) => {
let rr = res.data.data;
// callback 回调的作用是将所选择的视频的url显示在输入框中
callback(rr.filePath);
});
};
}
},
});
//监听外部传递进来的的数据变化,如果有变化就赋值然后调用父组件的方法同样赋值
watch(() => props.value, () => {
myValue.value = props.value;
emits("getContent", myValue.value,props.value);
}
);
//监听富文本中的数据变化
watch(() => myValue.value, () => {
emits("getContent", myValue.value);
}
);
//在onMounted中初始化编辑器
onMounted(() => {tinymce.init({})});
</script>
注意:图片如果上传不了可能是你没有图片的文件路劲
在上面代码有一段:
import { Upload } from "@/utils/http";
这是上传的图片路劲封装于http里面,http除了图片还有封装的get,post请求
http文件:
/**上传图片
* data -- 是图片数据
*/
function Upload(data: object) {
return instance.post("../upload/admin", data) //../上一级目录
}
export {
Upload
}
5.在页面使用
- 引入封装的文件
<template>
<Riachtext ref="editor" v-model="ruleForm.detail" @getContent="getContent"></Riachtext>
</template>
<script lang="ts" setup>
// 富文本
const getContent = (v: string) => {
ruleForm.detail = v
ruleFormRef.value?.clearValidate(['detail'])//移除某一个验证}/**富文本自定义赋值 */
}
<script>
注意:在v-model绑定的,于点击事件做是事,主要为了有个表单验证,以及去重验证