先上图,看最终效果:
项目需求:
产品期望实现【公文管理】其中发文拟文一块内容,就是用户撰写正文,再选择不同套红模板,最后拼接为一个对外发文格式,导出pdf文档。
综上所述,根据需求分析首先得有在线编辑功能富文本,并且该编辑器应该具有以下功能:
- 富文本编辑功能:用户可以使用富文本编辑器轻松地编辑和排版文本,最好编辑功能上得和word类似,包括插入图片、添加表格、超链接、字体样式等操作。
- 模板应用功能:用户可以选择不同的套红模板来应用于文档正文,以更好地适应不同的发文需求。
- 导出pdf功能:用户可以将文档导出为pdf格式,以便于打印、共享和存档。
技术选型:
- CKEditor: 一个功能强大的富文本编辑器,支持文本、图像、视频、表格等元素的编辑和排版。它可以集成各种插件,包括模板应用和pdf导出插件。
- TinyMCE: 一个受欢迎的富文本编辑器,可用于创建富文本编辑器和内容管理系统等。它提供了广泛的样式、格式和布局选项,也可以应用模板和导出pdf。
- Froala Editor: 一个现代化的文本编辑器,具有直观的界面和易于使用的编辑工具。它还提供了模板应用和pdf导出功能,支持HTML、Markdown和文本格式。
- Wangeditor: 具有丰富的编辑和排版选项,易于集成和定制。它还具有模板应用、插入图片、导出pdf等功能。
最终确定使用TinyMCE,它的优势主要体现在以下几个方面:
- 丰富的插件和扩展:TinyMCE 拥有大量的官方第三方插件和扩展,可以轻松地扩展和定制编辑器的功能,如图像上传、拓展工具栏、嵌入视频等。
- 可靠的稳定性: TinyMCE 是一款稳定成的富文本编辑器,它可以与各种浏览器和操作系统无缝兼容。并且,它的代码结构简单,易于维护和升级。
- 高度可定制:TinyMCE 可以根据不同的需求进行个性化的配置和定制,例如可以设置字体、大小、颜色、行间距等,支持多语言、多平台。
- 简单易用的接口和文档:TinyMCE 的官方文档非常详细,提供大量的示例和代码库,使开发者可以快速上手和集成。小巧简单的API使得任何人都可以轻松使用。
其他几款编辑器(如CKEditor、Froala Editor、Quill等)也具有自身的优势,但总体来说,TinyMCE 是一个成熟、稳定、灵活、易用的选择,适用于各种web应用的开发。
代码实现:
这里讲的实现方式是vue2使用Tinymce,vue3有些不一样,具体的可以网上搜搜看。
准备工作:
在开发之前得做前期学习准备工作:
tinymce中文文档:TinyMCE中文文档中文手册
tinymce官方文档:Documentation | Docs | TinyMCE
tinymce官方github:gitcode.net/mirrors/tin…
使用方式
下载依赖
npm i tinymce@5.10.3 @tinymce/tinymce-vue@3.2.8 -S
将依赖文件node_modules
中的tinymce
拷贝一份在public文件夹下面并创建tinymce,注意不需要拷贝所有文件,只需要拷贝下图标记的文件即可。
封装组件
在components创建对应组件imcoder-tinymce,相关路径:
src\components\Tinymce\imcoder-tinymce.vue
文件创建完成后将以下内容填写到该文件内:
<!-- 富文本 -->
<template>
<div class="tinymce-class">
<editor api-key="去官网申请key" v-model="content" :init="init" :disabled="disabled"></editor>
</div>
</template>
<script>
import Editor from "@tinymce/tinymce-vue";
import Tinymce from '@/components/Tinymce'
import { demo1,demo2 } from "../../views/overRed/templateCode";
export default {
components: {
Editor
},
props: {
value: {
type: String,
default: ""
},
disabled: {
type: Boolean,
default: false
},
plugins: {
type: [String, Array],
default:
"template print preview searchreplace autolink directionality visualblocks visualchars image link media code codesample table charmap hr nonbreaking insertdatetime advlist lists wordcount imagetools textpattern autosave autoresize"
},
toolbar: {
type: [String, Array],
default:
"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 | print preview template code| "
},
},
data() {
return {
//初始化配置
init: {},
content: this.value
};
},
created() {
let that = this
this.init = {
//selector: '#mytextarea',
//menubar: true, // 菜单栏显隐
statusbar:false,
language_url: "/zh_CN.js",
language: "zh_CN",
skin_url: "/tinymce/skins/ui/oxide",
//skin_url: '../../static/tinymce/skins/ui/oxide', // vue-cli2.x
// content_css: '../../../public/tinymce/skins/content/default/content.css',// vue-cli2.x
// content_css:['']
height: '1500%',
// min_height: '620',
// max_height: '620',
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 88px",
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,
resize: false,//禁止拖拽改变大小
// 对话框支持拖动
draggable_modal: true,
// 开启拖入功能,true:禁止拖入
paste_block_drop: true,
block_unsupported_drop: false,
// 允许粘贴图片
paste_data_images: true,
images_file_types: '*.*',
templates: [
{"title": "模板1", "description": "模板1", "content":demo1},
{"title": "模板2", "description": "模板2", "content": demo2}
// {"title": "模板3", "description": "S模板22", "url": "development.html"}
],
// // 是否开启自动保存,退出页面或刷新时提示
// autosave_ask_before_unload: true,
// // 自动保存时间间隔 秒
// autosave_interval: '30s',
// // 本地保存数据的有效期 分
// autosave_retention: "5m",
// 图片上传
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())
reserveTableFoodDescribe(formData).then(res => {
if (res.code === '10000') {
const file = res.data
success(file.url)
return
}
failure('上传失败')
}).catch(() => {
failure('上传出错')
})
},
init_instance_callback: function (editor) {
//5.0版本所有组件事件,均在此添加(keyup\keydown\Change……)
editor.on('Change', function (e) {
})
},
}
},
mounted() {
},
methods: {
},
watch: {
value(newValue) {
this.content = newValue;
},
content(newValue) {
this.$emit("input", newValue);
}
}
};
</script>
<!-- <style scoped lang="scss"> -->
<style>
/*后面的这些css为了调整样式更像word编辑器而设置的最开始可以不需要 */
/*
.tinymce-class{
margin:0px;
padding:0px;
width: calc(100% - 0px);
height:100%;
left: 0px;
top: 56px;
bottom:0px;
}
.tinymce-class :global(.tox-tinymce) {
height: 100% !important;
}
.tinymce-class :global( hr){
border-color: red !important;
border-style: solid;
border-width: 6px 0 0 0 !important;
}
*/
</style>
编辑器editor的api-key需要去官网申请key,因为采用从tinymce云上调用组件,如果没有的会提示错误,如下图
获取api-key可以参考这个大佬帖子blog.csdn.net/snans/artic…
调用组件
<template>
<div style="width: 100%;height: 100%;">
<Editor v-model="html" height="calc(100% - 20px)" class="wordCode"></Editor>
</div>
</template>
<script>
import Editor from "@/components/Tinymce/imcoder-tinymce.vue";
export default {
components: { Editor },
data() {
return {
html: '<p>呀哈哈</p>'
}
},
created() {
// tox.style =
},
mounted() {
},
computed: {},
methods: {
onclicktemp() {
console.log(this.html);
},
}
}
</script>
<style scoped>
/*后面的这些css为了调整样式更像word编辑器而设置的最开始可以不需要 */
/*
#top-container {
border-bottom: 1px solid #e8e8e8;
padding-left: 30px;
}
.wordCode :global(.tox .tox-edit-area) {
justify-content: center;
background-color: #f5f5f5;
height: 100%;
overflow-y: scroll;
}
.wordCode :global(.tox .tox-edit-area__iframe) {
width: 900px !important;
margin: 30px auto 150px auto;
background-color: #fff;
padding: 20px 50px 50px 50px;
border: 1px solid #e8e8e8;
box-shadow: 0 2px 10px rgb(0 0 0 / 12%);
overflow-y: scroll;
}
.wordCode :global(.tox .tox-edit-area__iframe html) {
overflow-y: scroll;
}
*/
</style>
引入组件后即可在网页上看到效果
调整样式
在这个时候可以看出Tinymce富文本默认样式,与想要的在线文档样式差距还是比较大的,刚好在这个时候看到了另外一个富文本wangEditor 5官方模拟腾讯文档的demo,并查看下对应github源码:
demo地址:www.wangeditor.com/demo/like-q…
wangEditor 5官方模拟腾讯文档效果图:
参考wangEditor思路,通过修改css样式调整编辑器和菜单栏的界面显示
把style中样式删除掉注释符
最后效果:
文档套红
兼容说明
再次说明为何采用这个TinyMCE:支持从 Microsoft 文档中复制和粘贴内容,并可以其转换为 HTML 格式。这使得从 Word 打印到 TinyMCE 编辑器中的过程更为顺畅,而且可以确保复制的内容与源文档一致。
除此之外,TinyMCE 还支持导入和导出 Microsoft Word 文档。导入功能将 Microsoft Word 文档转换为 HTML 格式,并将其放入TinyMCE 编辑器,方便编辑和修改。而导出功能则可以将 TinyMCE 编辑器中的内容导出为 Word 文档,以便在微软 Office 等其他应用程序中使用。
由于相关的兼容性我们可以复制一段word文档到编辑器看下效果,重点是保留文档格式和字体、字号等。
制作红头
当我们点击这个查看源码可以看到刚才输入的内容是html片段,并且可以修改任何html内容,基于此,我们可以根据自己想要的参考正式文件添加文档开头
将红色分割线部分前面内容保存一份用个单独templateCode.js文件做红头记录。
通过封装进行调用
import { demo1,demo2 } from "../../views/overRed/templateCode";
制作模板
TinyMCE有一个template模板属性,对应在界面上是【查看源码】旁边的按钮
详细使用参看官方文档:tinymce.ax-z.cn/plugins/tem…
实现效果,点击保存则插入到文档中:
也可以通过打印保存pdf文档
后续:
非常快乐找产品沟通这个需求讲述这个功能预演情况,然而产品重新讲解想法,期望用户通过上传word正文然后拼接套红,导出一个完整文档,你不能让客户在word上写一份又复制在平台上,这个方法不行pass掉,重新下解决方案。